src/Elastic.Documentation/GitCheckoutInformation.cs (112 lines of code) (raw):
// Licensed to Elasticsearch B.V under one or more agreements.
// Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
// See the LICENSE file in the project root for more information
using System.IO.Abstractions;
using System.Text.Json.Serialization;
using Microsoft.Extensions.Logging;
using SoftCircuits.IniFileParser;
namespace Elastic.Documentation;
public record GitCheckoutInformation
{
public static GitCheckoutInformation Unavailable { get; } = new()
{
Branch = "unavailable",
Remote = "unavailable",
Ref = "unavailable",
RepositoryName = "unavailable"
};
[JsonPropertyName("branch")]
public required string Branch { get; init; }
[JsonPropertyName("remote")]
public required string Remote { get; init; }
[JsonPropertyName("ref")]
public required string Ref { get; init; }
[JsonPropertyName("name")]
public string RepositoryName { get; init; } = "unavailable";
// manual read because libgit2sharp is not yet AOT ready
public static GitCheckoutInformation Create(IDirectoryInfo? source, IFileSystem fileSystem, ILogger? logger = null)
{
if (source is null)
return Unavailable;
if (fileSystem is not FileSystem)
{
return new GitCheckoutInformation
{
Branch = $"test-e35fcb27-5f60-4e",
Remote = "elastic/docs-builder",
Ref = "e35fcb27-5f60-4e",
RepositoryName = "docs-builder"
};
}
var fakeRef = Guid.NewGuid().ToString()[..16];
var gitConfig = Git(source, Path.Combine(".git", "config"));
if (!gitConfig.Exists)
{
logger?.LogInformation("Git checkout information not available.");
return Unavailable;
}
var head = Read(source, Path.Combine(".git", "HEAD")) ?? fakeRef;
var gitRef = head;
var branch = head.Replace("refs/heads/", string.Empty);
//not detached HEAD
if (head.StartsWith("ref:", StringComparison.OrdinalIgnoreCase))
{
head = head.Replace("ref: ", string.Empty);
gitRef = Read(source, Path.Combine(".git", head)) ?? fakeRef;
branch = branch.Replace("ref: ", string.Empty);
}
else
branch = Environment.GetEnvironmentVariable("GITHUB_PR_REF_NAME") ?? Environment.GetEnvironmentVariable("GITHUB_REF_NAME") ?? "detached/head";
var ini = new IniFile();
using var stream = gitConfig.OpenRead();
using var streamReader = new StreamReader(stream);
ini.Load(streamReader);
var remote = Environment.GetEnvironmentVariable("GITHUB_REPOSITORY");
if (string.IsNullOrEmpty(remote))
{
remote = BranchTrackingRemote(branch, ini);
logger?.LogInformation("Remote from branch: {GitRemote}", remote);
if (string.IsNullOrEmpty(remote))
{
remote = BranchTrackingRemote("main", ini);
logger?.LogInformation("Remote from main branch: {GitRemote}", remote);
}
if (string.IsNullOrEmpty(remote))
{
remote = BranchTrackingRemote("master", ini);
logger?.LogInformation("Remote from master branch: {GitRemote}", remote);
}
if (string.IsNullOrEmpty(remote))
{
remote = "elastic/docs-builder-unknown";
logger?.LogInformation("Remote from fallback: {GitRemote}", remote);
}
remote = remote.AsSpan().TrimEnd("git").TrimEnd('.').ToString();
}
var info = new GitCheckoutInformation
{
Ref = gitRef,
Branch = branch,
Remote = remote,
RepositoryName = remote.Split('/').Last()
};
logger?.LogInformation("-> Remote Name: {GitRemote}", info.Remote);
logger?.LogInformation("-> Repository Name: {RepositoryName}", info.RepositoryName);
return info;
IFileInfo Git(IDirectoryInfo directoryInfo, string path) =>
fileSystem.FileInfo.New(Path.Combine(directoryInfo.FullName, path));
string? Read(IDirectoryInfo directoryInfo, string path)
{
var gitPath = Git(directoryInfo, path).FullName;
return !fileSystem.File.Exists(gitPath)
? null
: fileSystem.File.ReadAllText(gitPath).Trim(Environment.NewLine.ToCharArray());
}
string BranchTrackingRemote(string b, IniFile c)
{
var sections = c.GetSections();
var branchSection = $"branch \"{b}\"";
if (!sections.Contains(branchSection))
return string.Empty;
var remoteName = ini.GetSetting(branchSection, "remote")?.Trim();
var remoteSection = $"remote \"{remoteName}\"";
remote = ini.GetSetting(remoteSection, "url");
return remote ?? string.Empty;
}
}
}