Compare commits
7 Commits
1823c5751c
...
43b4ef364e
Author | SHA1 | Date |
---|---|---|
|
43b4ef364e | 2 years ago |
|
b988194972 | 2 years ago |
|
86f3c3a5ec | 2 years ago |
|
ef330a3672 | 2 years ago |
|
c74958f1b8 | 2 years ago |
|
16bcbb313d | 2 years ago |
|
afdac21450 | 2 years ago |
@ -0,0 +1,45 @@
|
||||
namespace DocFx.Plugin.LastModified.Files;
|
||||
|
||||
/// <summary>
|
||||
/// A managed reference YAML document.
|
||||
/// </summary>
|
||||
public class ManagedReferenceDocument
|
||||
{
|
||||
public required ManagedReferenceItem[]? Items { get; set; }
|
||||
|
||||
public class ManagedReferenceItem
|
||||
{
|
||||
public required string Uid { get; set; }
|
||||
|
||||
public required string CommentId { get; set; }
|
||||
|
||||
public required string Id { get; set; }
|
||||
|
||||
public required string Name { get; set; }
|
||||
|
||||
public required string FullName { get; set; }
|
||||
|
||||
public required string NameWithType { get; set; }
|
||||
|
||||
public string? Type { get; set; }
|
||||
|
||||
public string? Parent { get; set; }
|
||||
|
||||
public string[]? Children { get; set; }
|
||||
|
||||
public string[]? Langs { get; set; }
|
||||
|
||||
public ManagedReferenceItemSource? Source { get; set; }
|
||||
|
||||
public string[]? Assemblies { get; set; }
|
||||
}
|
||||
|
||||
public class ManagedReferenceItemSource
|
||||
{
|
||||
public required string Id { get; set; }
|
||||
|
||||
public required string Path { get; set; }
|
||||
|
||||
public int? StartLine { get; set; }
|
||||
}
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
namespace DocFx.Plugin.LastModified;
|
||||
|
||||
using System;
|
||||
|
||||
public record LastModifiedInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the last modified date of the file.
|
||||
/// </summary>
|
||||
public DateTimeOffset LastModified { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the commit header, if any.
|
||||
/// </summary>
|
||||
public string CommitHeader { get; init; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the commit body, if any.
|
||||
/// </summary>
|
||||
public string CommitBody { get; init; } = string.Empty;
|
||||
}
|
@ -1,171 +1,56 @@
|
||||
using System;
|
||||
namespace DocFx.Plugin.LastModified;
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Composition;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using DocFx.Plugin.LastModified.Helpers;
|
||||
using HtmlAgilityPack;
|
||||
using LibGit2Sharp;
|
||||
using Microsoft.DocAsCode.Common;
|
||||
using Microsoft.DocAsCode.Plugins;
|
||||
|
||||
namespace DocFx.Plugin.LastModified
|
||||
using Docfx.Common;
|
||||
using Docfx.Plugins;
|
||||
using Processors;
|
||||
|
||||
/// <summary>
|
||||
/// Post-processor responsible for injecting last modified date according to commit or file modified date.
|
||||
/// </summary>
|
||||
[Export(nameof(LastModifiedPostProcessor), typeof(IPostProcessor))]
|
||||
public class LastModifiedPostProcessor : IPostProcessor
|
||||
{
|
||||
/// <summary>
|
||||
/// Post-processor responsible for injecting last modified date according to commit or file modified date.
|
||||
/// </summary>
|
||||
[Export(nameof(LastModifiedPostProcessor), typeof(IPostProcessor))]
|
||||
public class LastModifiedPostProcessor : IPostProcessor
|
||||
{
|
||||
private int _addedFiles;
|
||||
private Repository _repo;
|
||||
|
||||
public ImmutableDictionary<string, object> PrepareMetadata(ImmutableDictionary<string, object> metadata)
|
||||
=> metadata;
|
||||
|
||||
public Manifest Process(Manifest manifest, string outputFolder)
|
||||
{
|
||||
var versionInfo = Assembly.GetExecutingAssembly()
|
||||
.GetCustomAttribute<AssemblyInformationalVersionAttribute>()
|
||||
?.InformationalVersion ??
|
||||
Assembly.GetExecutingAssembly().GetName().Version?.ToString();
|
||||
Logger.LogInfo($"Version: {versionInfo}");
|
||||
Logger.LogInfo("Begin adding last modified date to items...");
|
||||
|
||||
// attempt to fetch git repo from the current project
|
||||
var gitDirectory = Repository.Discover(manifest.SourceBasePath);
|
||||
if (gitDirectory != null) _repo = new Repository(gitDirectory);
|
||||
private int _addedFiles;
|
||||
|
||||
foreach (var manifestItem in manifest.Files.Where(x => x.DocumentType == "Conceptual"))
|
||||
foreach (var manifestItemOutputFile in manifestItem.OutputFiles)
|
||||
{
|
||||
var sourcePath = Path.Combine(manifest.SourceBasePath, manifestItem.SourceRelativePath);
|
||||
var outputPath = Path.Combine(outputFolder, manifestItemOutputFile.Value.RelativePath);
|
||||
if (_repo != null)
|
||||
{
|
||||
var commitInfo = _repo.GetCommitInfo(sourcePath);
|
||||
if (commitInfo != null)
|
||||
{
|
||||
Logger.LogVerbose("Assigning commit date...");
|
||||
var lastModified = commitInfo.Author.When;
|
||||
|
||||
var commitHeaderBuilder = new StringBuilder();
|
||||
Logger.LogVerbose("Appending commit author and email...");
|
||||
commitHeaderBuilder.AppendLine($"Author: {commitInfo.Author.Name}");
|
||||
Logger.LogVerbose("Appending commit SHA...");
|
||||
commitHeaderBuilder.AppendLine($"Commit: {commitInfo.Sha}");
|
||||
|
||||
var commitHeader = commitHeaderBuilder.ToString();
|
||||
// truncate to 200 in case of huge commit body
|
||||
var commitBody = commitInfo.Message.Truncate(300);
|
||||
Logger.LogVerbose($"Writing {lastModified} with reason for {outputPath}...");
|
||||
WriteModifiedDate(outputPath, lastModified, commitHeader, commitBody);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
private IEnumerable<IProcessor> Processors { get; } = new List<IProcessor>
|
||||
{
|
||||
new ConceptualProcessor(),
|
||||
new ManagedReferenceProcessor(),
|
||||
};
|
||||
|
||||
var fileLastModified = File.GetLastWriteTimeUtc(sourcePath);
|
||||
Logger.LogVerbose($"Writing {fileLastModified} for {outputPath}...");
|
||||
WriteModifiedDate(outputPath, fileLastModified);
|
||||
}
|
||||
/// <inheritdoc />
|
||||
public ImmutableDictionary<string, object> PrepareMetadata(ImmutableDictionary<string, object> metadata)
|
||||
=> metadata;
|
||||
|
||||
// dispose repo after usage
|
||||
_repo?.Dispose();
|
||||
/// <inheritdoc />
|
||||
public Manifest Process(Manifest manifest, string outputFolder)
|
||||
{
|
||||
var versionInfo = Assembly.GetExecutingAssembly().GetName().Version;
|
||||
|
||||
Logger.LogInfo($"Added modification date to {_addedFiles} conceptual articles.");
|
||||
return manifest;
|
||||
}
|
||||
Logger.LogInfo($"Version: {versionInfo}");
|
||||
Logger.LogInfo("Begin adding last modified date to items...");
|
||||
|
||||
private void WriteModifiedDate(string outputPath, DateTimeOffset modifiedDate, string commitHeader = null,
|
||||
string commitBody = null)
|
||||
foreach (var manifestItem in manifest.Files)
|
||||
{
|
||||
if (outputPath == null) throw new ArgumentNullException(nameof(outputPath));
|
||||
|
||||
// load the document
|
||||
var htmlDoc = new HtmlDocument();
|
||||
htmlDoc.Load(outputPath);
|
||||
|
||||
// check for article container
|
||||
var articleNode = htmlDoc.DocumentNode.SelectSingleNode("//article[contains(@class, 'content wrap')]");
|
||||
if (articleNode == null)
|
||||
var processor = Processors.FirstOrDefault(p => p.Supports(manifestItem.Type));
|
||||
if (processor == null)
|
||||
{
|
||||
Logger.LogDiagnostic("ArticleNode not found, returning.");
|
||||
return;
|
||||
Logger.LogDiagnostic($"No processor found for {manifestItem.Type}, skipping.");
|
||||
continue;
|
||||
}
|
||||
|
||||
var paragraphNode = htmlDoc.CreateElement("p");
|
||||
paragraphNode.InnerHtml = $"This page was last modified at {modifiedDate} (UTC).";
|
||||
var separatorNode = htmlDoc.CreateElement("hr");
|
||||
articleNode.AppendChild(separatorNode);
|
||||
articleNode.AppendChild(paragraphNode);
|
||||
|
||||
if (!string.IsNullOrEmpty(commitHeader))
|
||||
if (processor.TryProcess(manifest, manifestItem, outputFolder))
|
||||
{
|
||||
// inject collapsible container script
|
||||
InjectCollapseScript(htmlDoc);
|
||||
|
||||
// create collapse container
|
||||
var collapsibleNode = htmlDoc.CreateElement("div");
|
||||
collapsibleNode.SetAttributeValue("class", "collapse-container last-modified");
|
||||
collapsibleNode.SetAttributeValue("id", "accordion");
|
||||
var reasonHeaderNode = htmlDoc.CreateElement("span");
|
||||
reasonHeaderNode.InnerHtml = "<span class=\"arrow-r\"></span>Commit Message";
|
||||
var reasonContainerNode = htmlDoc.CreateElement("div");
|
||||
|
||||
// inject header
|
||||
var preCodeBlockNode = htmlDoc.CreateElement("pre");
|
||||
var codeBlockNode = htmlDoc.CreateElement("code");
|
||||
codeBlockNode.InnerHtml = commitHeader;
|
||||
preCodeBlockNode.AppendChild(codeBlockNode);
|
||||
reasonContainerNode.AppendChild(preCodeBlockNode);
|
||||
|
||||
// inject body
|
||||
preCodeBlockNode = htmlDoc.CreateElement("pre");
|
||||
codeBlockNode = htmlDoc.CreateElement("code");
|
||||
codeBlockNode.SetAttributeValue("class", "xml");
|
||||
codeBlockNode.InnerHtml = commitBody;
|
||||
preCodeBlockNode.AppendChild(codeBlockNode);
|
||||
reasonContainerNode.AppendChild(preCodeBlockNode);
|
||||
|
||||
// inject the entire block
|
||||
collapsibleNode.AppendChild(reasonHeaderNode);
|
||||
collapsibleNode.AppendChild(reasonContainerNode);
|
||||
articleNode.AppendChild(collapsibleNode);
|
||||
_addedFiles++;
|
||||
}
|
||||
|
||||
htmlDoc.Save(outputPath);
|
||||
_addedFiles++;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Injects script required for collapsible dropdown menu.
|
||||
/// </summary>
|
||||
/// <seealso cref="!:https://github.com/jordnkr/collapsible" />
|
||||
private static void InjectCollapseScript(HtmlDocument htmlDoc)
|
||||
{
|
||||
var bodyNode = htmlDoc.DocumentNode.SelectSingleNode("//body");
|
||||
|
||||
var accordionNode = htmlDoc.CreateElement("script");
|
||||
accordionNode.InnerHtml = @"
|
||||
$( function() {
|
||||
$( ""#accordion"" ).collapsible();
|
||||
} );";
|
||||
bodyNode.AppendChild(accordionNode);
|
||||
|
||||
var collapsibleScriptNode = htmlDoc.CreateElement("script");
|
||||
collapsibleScriptNode.SetAttributeValue("type", "text/javascript");
|
||||
collapsibleScriptNode.SetAttributeValue("src",
|
||||
"https://cdn.rawgit.com/jordnkr/collapsible/master/jquery.collapsible.min.js");
|
||||
bodyNode.AppendChild(collapsibleScriptNode);
|
||||
|
||||
var headNode = htmlDoc.DocumentNode.SelectSingleNode("//head");
|
||||
var collapsibleCssNode = htmlDoc.CreateElement("link");
|
||||
collapsibleCssNode.SetAttributeValue("rel", "stylesheet");
|
||||
collapsibleCssNode.SetAttributeValue("href",
|
||||
"https://cdn.rawgit.com/jordnkr/collapsible/master/collapsible.css");
|
||||
headNode.AppendChild(collapsibleCssNode);
|
||||
}
|
||||
Logger.LogInfo($"Added modification date to {_addedFiles} articles.");
|
||||
return manifest;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,116 @@
|
||||
namespace DocFx.Plugin.LastModified.Processors;
|
||||
|
||||
using Docfx.Common;
|
||||
using Docfx.Plugins;
|
||||
using HtmlAgilityPack;
|
||||
|
||||
/// <summary>
|
||||
/// An abstract processor for adding last modified date to articles, contains common methods.
|
||||
/// </summary>
|
||||
public abstract class AbstractProcessor : IProcessor
|
||||
{
|
||||
/// <summary>
|
||||
/// Determines whether this processor supports the given type.
|
||||
/// </summary>
|
||||
/// <param name="type">The type of manifest item.</param>
|
||||
/// <returns>True if this processor supports the given type, false otherwise.</returns>
|
||||
public abstract bool Supports(string type);
|
||||
|
||||
/// <summary>
|
||||
/// Processes the given manifest item.
|
||||
/// </summary>
|
||||
/// <param name="manifest">The <see cref="Manifest"/> that contains the manifest item.</param>
|
||||
/// <param name="manifestItem">The <see cref="ManifestItem"/> to process.</param>
|
||||
/// <param name="outputFolder">The output folder.</param>
|
||||
public abstract void Process(Manifest manifest, ManifestItem manifestItem, string outputFolder);
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to process the given manifest item.
|
||||
/// </summary>
|
||||
/// <param name="manifest">The <see cref="Manifest"/> that contains the manifest item.</param>
|
||||
/// <param name="manifestItem">The <see cref="ManifestItem"/> to process.</param>
|
||||
/// <param name="outputFolder">The output folder.</param>
|
||||
/// <returns>True if the manifest item was processed, false otherwise.</returns>
|
||||
public bool TryProcess(Manifest manifest, ManifestItem manifestItem, string outputFolder)
|
||||
{
|
||||
if (!Supports(manifestItem.Type))
|
||||
{
|
||||
Logger.LogVerbose($"Skipping {manifestItem.Type}...");
|
||||
return false;
|
||||
}
|
||||
|
||||
Logger.LogDiagnostic($"Processing {manifestItem.Type}...");
|
||||
Process(manifest, manifestItem, outputFolder);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Builds the last modified info for the given file path.
|
||||
/// </summary>
|
||||
/// <param name="filePath">The file path.</param>
|
||||
/// <returns>The <see cref="LastModifiedInfo"/> for the given file path.</returns>
|
||||
protected abstract LastModifiedInfo GetLastModifiedInfo(string filePath);
|
||||
|
||||
/// <summary>
|
||||
/// Modifies the given document with the given last modified info.
|
||||
/// </summary>
|
||||
/// <param name="filePath">The file path to the document.</param>
|
||||
/// <param name="lastModifiedInfo">The <see cref="LastModifiedInfo"/> to use.</param>
|
||||
protected virtual void ModifyDocument(string filePath, LastModifiedInfo lastModifiedInfo)
|
||||
{
|
||||
var htmlDocument = new HtmlDocument();
|
||||
htmlDocument.Load(filePath);
|
||||
|
||||
var articleNode = htmlDocument.DocumentNode.SelectSingleNode("//article");
|
||||
if (articleNode == null)
|
||||
{
|
||||
Logger.LogWarning($"No article node found in {filePath}.");
|
||||
return;
|
||||
}
|
||||
|
||||
var separatorNode = htmlDocument.CreateElement("hr");
|
||||
articleNode.AppendChild(separatorNode);
|
||||
|
||||
var lastModifiedNode = htmlDocument.CreateElement("div");
|
||||
lastModifiedNode.SetAttributeValue("class", "last-modified");
|
||||
articleNode.AppendChild(lastModifiedNode);
|
||||
|
||||
var paragraphNode = htmlDocument.CreateElement("p");
|
||||
paragraphNode.InnerHtml = $"This page was last modified at {lastModifiedInfo.LastModified} (UTC).";
|
||||
lastModifiedNode.AppendChild(paragraphNode);
|
||||
|
||||
if (string.IsNullOrEmpty(lastModifiedInfo.CommitHeader))
|
||||
{
|
||||
htmlDocument.Save(filePath);
|
||||
return;
|
||||
}
|
||||
|
||||
var collapsibleNode = htmlDocument.CreateElement("details");
|
||||
lastModifiedNode.AppendChild(collapsibleNode);
|
||||
|
||||
var reasonHeaderNode = htmlDocument.CreateElement("summary");
|
||||
reasonHeaderNode.SetAttributeValue("style", "display: list-item;");
|
||||
reasonHeaderNode.InnerHtml = "Commit Message";
|
||||
collapsibleNode.AppendChild(reasonHeaderNode);
|
||||
|
||||
var reasonContainerNode = htmlDocument.CreateElement("div");
|
||||
collapsibleNode.AppendChild(reasonContainerNode);
|
||||
|
||||
var preCodeBlockNode = htmlDocument.CreateElement("pre");
|
||||
var codeBlockNode = htmlDocument.CreateElement("code");
|
||||
codeBlockNode.InnerHtml = lastModifiedInfo.CommitHeader;
|
||||
preCodeBlockNode.AppendChild(codeBlockNode);
|
||||
reasonContainerNode.AppendChild(preCodeBlockNode);
|
||||
|
||||
if (!string.IsNullOrEmpty(lastModifiedInfo.CommitBody))
|
||||
{
|
||||
preCodeBlockNode = htmlDocument.CreateElement("pre");
|
||||
codeBlockNode = htmlDocument.CreateElement("code");
|
||||
codeBlockNode.InnerHtml = lastModifiedInfo.CommitBody;
|
||||
preCodeBlockNode.AppendChild(codeBlockNode);
|
||||
reasonContainerNode.AppendChild(preCodeBlockNode);
|
||||
}
|
||||
|
||||
htmlDocument.Save(filePath);
|
||||
}
|
||||
}
|
@ -0,0 +1,74 @@
|
||||
namespace DocFx.Plugin.LastModified.Processors;
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using Docfx.Common;
|
||||
using Docfx.Plugins;
|
||||
using Helpers;
|
||||
using LibGit2Sharp;
|
||||
|
||||
/// <summary>
|
||||
/// Processor for adding last modified date to conceptual articles.
|
||||
/// </summary>
|
||||
public class ConceptualProcessor : AbstractProcessor
|
||||
{
|
||||
private Repository? _repo;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool Supports(string type)
|
||||
{
|
||||
return type == "Conceptual";
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Process(Manifest manifest, ManifestItem manifestItem, string outputFolder)
|
||||
{
|
||||
var repository = Repository.Discover(manifest.SourceBasePath);
|
||||
if (repository != null)
|
||||
{
|
||||
_repo = new Repository(repository);
|
||||
}
|
||||
|
||||
var sourcePath = Path.Combine(manifest.SourceBasePath, manifestItem.SourceRelativePath);
|
||||
var outputPath = Path.Combine(outputFolder, manifestItem.Output[".html"].RelativePath);
|
||||
|
||||
var lastModifiedInfo = GetLastModifiedInfo(sourcePath);
|
||||
|
||||
ModifyDocument(outputPath, lastModifiedInfo);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override LastModifiedInfo GetLastModifiedInfo(string filePath)
|
||||
{
|
||||
var lastModified = DateTimeOffset.MinValue;
|
||||
var commitHeader = string.Empty;
|
||||
var commitBody = string.Empty;
|
||||
|
||||
if (_repo?.GetCommitInfo(filePath) is { } commitInfo)
|
||||
{
|
||||
lastModified = commitInfo.Author.When;
|
||||
Logger.LogDiagnostic($"Last modified date: {lastModified} (UTC)");
|
||||
|
||||
var commitHeaderBuilder = new StringBuilder();
|
||||
commitHeaderBuilder.AppendLine($"Author: {commitInfo.Author.Name}");
|
||||
commitHeaderBuilder.AppendLine($"Commit: {commitInfo.Sha}");
|
||||
|
||||
commitHeader = commitHeaderBuilder.ToString();
|
||||
commitBody = commitInfo.Message.Truncate(300);
|
||||
}
|
||||
|
||||
if (lastModified == DateTimeOffset.MinValue)
|
||||
{
|
||||
lastModified = File.GetLastWriteTimeUtc(filePath);
|
||||
Logger.LogVerbose($"Last modified date: {lastModified} (UTC)");
|
||||
}
|
||||
|
||||
return new LastModifiedInfo
|
||||
{
|
||||
LastModified = lastModified,
|
||||
CommitHeader = commitHeader,
|
||||
CommitBody = commitBody,
|
||||
};
|
||||
}
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
namespace DocFx.Plugin.LastModified.Processors;
|
||||
|
||||
using Docfx.Plugins;
|
||||
|
||||
public interface IProcessor
|
||||
{
|
||||
bool Supports(string type);
|
||||
|
||||
void Process(Manifest manifest, ManifestItem manifestItem, string outputFolder);
|
||||
|
||||
bool TryProcess(Manifest manifest, ManifestItem manifestItem, string outputFolder);
|
||||
}
|
@ -0,0 +1,93 @@
|
||||
namespace DocFx.Plugin.LastModified.Processors;
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using Docfx.Common;
|
||||
using Docfx.Plugins;
|
||||
using Files;
|
||||
using Helpers;
|
||||
using LibGit2Sharp;
|
||||
using YamlDotNet.Serialization;
|
||||
using YamlDotNet.Serialization.NamingConventions;
|
||||
|
||||
/// <summary>
|
||||
/// Processor for adding last modified date to managed reference articles.
|
||||
/// </summary>
|
||||
public class ManagedReferenceProcessor : AbstractProcessor
|
||||
{
|
||||
private readonly Deserializer _deserializer = Deserializer.FromValueDeserializer(
|
||||
new DeserializerBuilder()
|
||||
.WithNamingConvention(CamelCaseNamingConvention.Instance)
|
||||
.IgnoreUnmatchedProperties()
|
||||
.BuildValueDeserializer());
|
||||
|
||||
private Repository? _repo;
|
||||
private Manifest? _manifest;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool Supports(string type)
|
||||
{
|
||||
return type == "ManagedReference";
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Process(Manifest manifest, ManifestItem manifestItem, string outputFolder)
|
||||
{
|
||||
var repository = Repository.Discover(manifest.SourceBasePath);
|
||||
if (repository != null)
|
||||
{
|
||||
_repo = new Repository(repository);
|
||||
}
|
||||
|
||||
_manifest = manifest;
|
||||
|
||||
var sourcePath = Path.Combine(manifest.SourceBasePath, manifestItem.SourceRelativePath);
|
||||
var outputPath = Path.Combine(outputFolder, manifestItem.Output[".html"].RelativePath);
|
||||
|
||||
var lastModifiedInfo = GetLastModifiedInfo(sourcePath);
|
||||
|
||||
ModifyDocument(outputPath, lastModifiedInfo);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override LastModifiedInfo GetLastModifiedInfo(string filePath)
|
||||
{
|
||||
var yml = File.ReadAllText(filePath);
|
||||
var managedReferenceDocument = _deserializer.Deserialize<ManagedReferenceDocument?>(yml);
|
||||
var itemSource = managedReferenceDocument?.Items?.FirstOrDefault(i => !string.IsNullOrEmpty(i.Source?.Path));
|
||||
var itemSourcePath = itemSource?.Source?.Path ?? string.Empty;
|
||||
|
||||
var lastModified = DateTimeOffset.MinValue;
|
||||
var commitHeader = string.Empty;
|
||||
var commitBody = string.Empty;
|
||||
|
||||
var sourcePath = Path.Combine(_manifest?.SourceBasePath ?? string.Empty, itemSourcePath);
|
||||
|
||||
if (!string.IsNullOrEmpty(itemSourcePath) && _repo?.GetCommitInfo(sourcePath) is { } commitInfo)
|
||||
{
|
||||
lastModified = commitInfo.Author.When;
|
||||
Logger.LogDiagnostic($"Last modified date: {lastModified} (UTC)");
|
||||
|
||||
var commitHeaderBuilder = new System.Text.StringBuilder();
|
||||
commitHeaderBuilder.AppendLine($"Author: {commitInfo.Author.Name}");
|
||||
commitHeaderBuilder.AppendLine($"Commit: {commitInfo.Sha}");
|
||||
|
||||
commitHeader = commitHeaderBuilder.ToString();
|
||||
commitBody = commitInfo.Message.Truncate(300);
|
||||
}
|
||||
|
||||
if (lastModified == DateTimeOffset.MinValue)
|
||||
{
|
||||
lastModified = File.GetLastWriteTimeUtc(sourcePath);
|
||||
Logger.LogVerbose($"Last modified date: {lastModified} (UTC)");
|
||||
}
|
||||
|
||||
return new LastModifiedInfo
|
||||
{
|
||||
LastModified = lastModified,
|
||||
CommitHeader = commitHeader,
|
||||
CommitBody = commitBody,
|
||||
};
|
||||
}
|
||||
}
|
@ -0,0 +1 @@
|
||||
Based on https://github.com/dhkatz/DocFx.Plugin.LastModified
|
Loading…
Reference in New Issue