Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Branch name for git detached head #1098

Merged
merged 9 commits into from
Jan 11, 2017
26 changes: 23 additions & 3 deletions Documentation/tutorial/docfx_getting_started.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,27 @@ namespace WebApplication1

*Step4.* Right click on the website project, and click *View* -> *View in Browser*, navigate to `/_site` sub URL to view your website!

4. Build from source code
4. Use *DocFX* with a Build Server
---------------

*DocFX* can be used in a Continuous Integration environment.

Most build systems do not checkout the branch that is being built, but
use a `detached head` for the specific commit. DoxFX needs the the branch name to implement the `View Source` link in the API documentation.

Setting the environment variable `DOCFX_SOURCE_BRANCH_NAME` tells DocFX which branch name to use.

Many build systems set an environment variable with the branch name. DocFX uses the following:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Support DOCFX_SOURCE_BRANCH_NAME is enough? Use documentation to tell CI users how to configure environment variables with different build systems?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I prefer to have docfx run on common the build system without any special instructions.


- `APPVEYOR_REPO_BRANCH` - [AppVeyor](https://www.appveyor.com/)
- `BUILD_SOURCEBRANCHNAME` - [Visual Studio Online](https://www.visualstudio.com/vso/)
- `CI_BUILD_REF_NAME` - [GitLab CI](https://about.gitlab.com/gitlab-ci/)
- `Git_Branch` - [TeamCity](https://www.jetbrains.com/teamcity/)
- `GIT_BRANCH` - [Jenkins](https://jenkins.io/)
- `GIT_LOCAL_BRANCH` - [Jenkins](https://jenkins.io/)


5. Build from source code
----------------
As a prerequisite, you need:
- [Microsoft Build Tools 2015](https://www.microsoft.com/en-us/download/details.aspx?id=48159)
Expand All @@ -80,7 +100,7 @@ As a prerequisite, you need:

*Step4.* Follow steps in #2, #3, #4 to use *DocFX* in command-line, IDE or .NET Core.

5. A seed project to play with *DocFX*
6. A seed project to play with *DocFX*
-------------------------
Here is a seed project https://github.com/docascode/docfx-seed. It contains

Expand All @@ -93,7 +113,7 @@ Here is a seed project https://github.com/docascode/docfx-seed. It contains
> [!Tip]
> It is a good practice to separate files with different type into different folders.

6. Q&A
7. Q&A
-------------------------
1. Q: How do I quickly reference APIs from other APIs or conceptual files?
A: Use `@uid` syntax.
Expand Down
103 changes: 75 additions & 28 deletions src/Microsoft.DocAsCode.Common/Git/GitUtility.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
// Copyright (c) Microsoft. All rights reserved.
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

namespace Microsoft.DocAsCode.Common.Git
{
using System;
using System.Linq;
using System.IO;
using System.Text;
using System.Collections.Concurrent;
Expand All @@ -22,6 +23,16 @@ public static class GitUtility
private static readonly string GetOriginUrlCommand = "config --get remote.origin.url";
private static readonly string GetLocalHeadIdCommand = "rev-parse HEAD";
private static readonly string GetRemoteHeadIdCommand = "rev-parse @{u}";

private static readonly string[] BuildSystemBranchName = new[]
{
"APPVEYOR_REPO_BRANCH", // AppVeyor
"Git_Branch", // Team City
"CI_BUILD_REF_NAME", // GitLab CI
"GIT_LOCAL_BRANCH", // Jenkins
"GIT_BRANCH", // Jenkins
"BUILD_SOURCEBRANCHNAME" // VSO Agent
};

private static readonly ConcurrentDictionary<string, GitRepoInfo> Cache = new ConcurrentDictionary<string, GitRepoInfo>();

Expand Down Expand Up @@ -112,12 +123,70 @@ private static GitDetail GetFileDetailCore(string filePath)
private static GitRepoInfo GetRepoInfoCore(string directory)
{
var repoRootPath = RunGitCommandAndGetFirstLine(directory, GetRepoRootCommand);
var localBranch = GetLocalBranchName(repoRootPath);
var originUrl = RunGitCommandAndGetFirstLine(repoRootPath, GetOriginUrlCommand);
var repoInfo = new GitRepoInfo
{
// TODO: remove commit id to avoid config hash changed
//LocalHeadCommitId = RunGitCommandAndGetFirstLine(repoRootPath, GetLocalHeadIdCommand),
//RemoteHeadCommitId = TryRunGitCommandAndGetFirstLine(repoRootPath, GetRemoteHeadIdCommand),
RemoteOriginUrl = originUrl,
RepoRootPath = repoRootPath
};

return GetBranchNames(repoInfo);
}

private static GitRepoInfo GetBranchNames(GitRepoInfo repo)
{
bool isDetachedHead = false;

// The "docfx..". environment variable specifies the branch name to use.
var localBranch = Environment.GetEnvironmentVariable("DOCFX_SOURCE_BRANCH_NAME");
if (!string.IsNullOrEmpty(localBranch))
{
Logger.LogInfo($"Using branch '{localBranch}' from the environment variable DOCFX_SOURCE_BRANCH_NAME.");
}

// Many build systems use a "detached head", which means that the normal git commands
// to get branch names do not work. Thankfully, they set an environment variable.
if (string.IsNullOrEmpty(localBranch))
{
isDetachedHead = "HEAD" == RunGitCommandAndGetFirstLine(repo.RepoRootPath, GetLocalBranchCommand);
if (isDetachedHead)
{
foreach (var name in BuildSystemBranchName)
{
localBranch = Environment.GetEnvironmentVariable(name);
if (!string.IsNullOrEmpty(localBranch))
{
Logger.LogInfo($"Using branch '{localBranch}' from the environment variable {name}.");
break;
}
}
}
}

// Fallback to using commit id.
if (isDetachedHead && string.IsNullOrEmpty(localBranch))
{
localBranch = RunGitCommandAndGetFirstLine(repo.RepoRootPath, GetLocalBranchCommitIdCommand);
Logger.LogInfo("Fallback to use commit id as the branch name.");
}

// If an override, then remote branch name is same as local branch name.
if (!string.IsNullOrEmpty(localBranch))
{
repo.LocalBranch = localBranch;
repo.RemoteBranch = localBranch;
return repo;
}

// Not a detached head. Use standard git commands to get the branch names.
localBranch = RunGitCommandAndGetFirstLine(repo.RepoRootPath, GetLocalBranchCommand);
string remoteBranch;
try
{
remoteBranch = RunGitCommandAndGetFirstLine(repoRootPath, GetRemoteBranchCommand);
remoteBranch = RunGitCommandAndGetFirstLine(repo.RepoRootPath, GetRemoteBranchCommand);
var index = remoteBranch.IndexOf('/');
if (index > 0)
{
Expand All @@ -130,31 +199,9 @@ private static GitRepoInfo GetRepoInfoCore(string directory)
remoteBranch = localBranch;
}

var originUrl = RunGitCommandAndGetFirstLine(repoRootPath, GetOriginUrlCommand);

return new GitRepoInfo
{
LocalBranch = localBranch,
// TODO: remove commit id to avoid config hash changed
//LocalHeadCommitId = RunGitCommandAndGetFirstLine(repoRootPath, GetLocalHeadIdCommand),
//RemoteHeadCommitId = TryRunGitCommandAndGetFirstLine(repoRootPath, GetRemoteHeadIdCommand),
RemoteOriginUrl = originUrl,
RepoRootPath = repoRootPath,
RemoteBranch = remoteBranch,
};
}

private static string GetLocalBranchName(string repoRootPath)
{
var localBranch = RunGitCommandAndGetFirstLine(repoRootPath, GetLocalBranchCommand);

// Fallback to use commit id
if (localBranch.Equals("HEAD"))
{
localBranch = RunGitCommandAndGetFirstLine(repoRootPath, GetLocalBranchCommitIdCommand);
Logger.LogInfo("Fallback to use commit id as the branch name.");
}
return localBranch;
repo.LocalBranch = localBranch;
repo.RemoteBranch = remoteBranch;
return repo;
}

private static void ProcessErrorMessage(string message)
Expand Down
38 changes: 38 additions & 0 deletions test/Microsoft.DocAsCode.Common.Tests/GitUtilityTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

namespace Microsoft.DocAsCode.Common.Tests
{
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;

using Xunit;
using YamlDotNet.Core;

using Microsoft.DocAsCode.Common.Git;
using Microsoft.DocAsCode.YamlSerialization;

[Trait("Owner", "makaretu")]
public class GitUtilityTest
{
[Fact]
public void Environment_ForBranchName()
{
const string envName = "DOCFX_SOURCE_BRANCH_NAME";
var original = Environment.GetEnvironmentVariable(envName);
try
{
Environment.SetEnvironmentVariable(envName, "special-branch");
var info = GitUtility.GetFileDetail(Directory.GetCurrentDirectory());
Assert.Equal("special-branch", info.RemoteBranch);
}
finally
{
Environment.SetEnvironmentVariable(envName, original);
}
}

}
}