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

Add Azure Pipelines group logging commands #4164

Merged
merged 1 commit into from
Sep 29, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,48 @@ public void Should_Log_Error_Message_With_Data()
Assert.Contains(fixture.Writer.Entries, m => m == $"##vso[task.logissue sourcepath=./code.cs;linenumber=1;columnnumber=2;code=3;type=error;]build error");
}

[Fact]
public void Should_Begin_Group_With_Name()
{
// Given
var fixture = new AzurePipelinesFixture();
var service = fixture.CreateAzurePipelinesService();

// When
service.Commands.BeginGroup("Example Group");

// Then
Assert.Contains(fixture.Writer.Entries, m => m == $"##[group]Example Group");
}

[Fact]
public void Should_End_Group()
{
// Given
var fixture = new AzurePipelinesFixture();
var service = fixture.CreateAzurePipelinesService();

// When
service.Commands.EndGroup();

// Then
Assert.Contains(fixture.Writer.Entries, m => m == $"##[endgroup]");
}

[Fact]
public void Should_Section_With_Name()
{
// Given
var fixture = new AzurePipelinesFixture();
var service = fixture.CreateAzurePipelinesService();

// When
service.Commands.Section("Example Section");

// Then
Assert.Contains(fixture.Writer.Entries, m => m == $"##[section]Example Section");
}

[Fact]
public void Should_Set_Current_Progress()
{
Expand All @@ -152,7 +194,7 @@ public void Should_Complete_Current_Task()
service.Commands.CompleteCurrentTask();

// Then
Assert.Contains(fixture.Writer.Entries, m => m == $"##vso[task.complete ]DONE");
Assert.Contains(fixture.Writer.Entries, m => m == $"##vso[task.complete]DONE");
}

[Fact]
Expand Down Expand Up @@ -281,7 +323,7 @@ public void Should_Upload_Task_Summary()
service.Commands.UploadTaskSummary("./summary.md");

// Then
Assert.Contains(fixture.Writer.Entries, m => m == $"##vso[task.uploadsummary ]{path}");
Assert.Contains(fixture.Writer.Entries, m => m == $"##vso[task.uploadsummary]{path}");
}

[Fact]
Expand All @@ -296,7 +338,7 @@ public void Should_Upload_Task_Log()
service.Commands.UploadTaskLogFile("./logs/task.log");

// Then
Assert.Contains(fixture.Writer.Entries, m => m == $"##vso[task.uploadfile ]{path}");
Assert.Contains(fixture.Writer.Entries, m => m == $"##vso[task.uploadfile]{path}");
}

[Theory]
Expand Down Expand Up @@ -432,7 +474,7 @@ public void Should_Upload_Build_Log()
service.Commands.UploadBuildLogFile("./dist/buildlog.txt");

// Then
Assert.Contains(fixture.Writer.Entries, m => m == $"##vso[build.uploadlog ]{path}");
Assert.Contains(fixture.Writer.Entries, m => m == $"##vso[build.uploadlog]{path}");
}

[Fact]
Expand All @@ -446,7 +488,7 @@ public void Should_Update_Build_Number()
service.Commands.UpdateBuildNumber("CIBuild_1");

// Then
Assert.Contains(fixture.Writer.Entries, m => m == "##vso[build.updatebuildnumber ]CIBuild_1");
Assert.Contains(fixture.Writer.Entries, m => m == "##vso[build.updatebuildnumber]CIBuild_1");
}

[Fact]
Expand All @@ -460,7 +502,7 @@ public void Should_Add_Build_Tag()
service.Commands.AddBuildTag("Stable");

// Then
Assert.Contains(fixture.Writer.Entries, m => m == "##vso[build.addbuildtag ]Stable");
Assert.Contains(fixture.Writer.Entries, m => m == "##vso[build.addbuildtag]Stable");
}

[Fact]
Expand Down
26 changes: 25 additions & 1 deletion src/Cake.Common/Build/AzurePipelines/AzurePipelinesCommands.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ namespace Cake.Common.Build.AzurePipelines
/// </summary>
public sealed class AzurePipelinesCommands : IAzurePipelinesCommands
{
private const string FormatPrefix = "##[";
private const string MessagePrefix = "##vso[";
private const string MessagePostfix = "]";

Expand Down Expand Up @@ -69,6 +70,24 @@ public void WriteError(string message, AzurePipelinesMessageData data)
WriteLoggingCommand("task.logissue", properties, message);
}

/// <inheritdoc/>
public void BeginGroup(string name)
{
WriteFormatCommand("group", name);
}

/// <inheritdoc/>
public void EndGroup()
{
WriteFormatCommand("endgroup", string.Empty);
}

/// <inheritdoc/>
public void Section(string name)
{
WriteFormatCommand("section", name);
}

/// <inheritdoc/>
public void SetProgress(int progress, string currentOperation)
{
Expand Down Expand Up @@ -287,9 +306,14 @@ public void PublishCodeCoverage(FilePath summaryFilePath, Action<AzurePipelinesP
PublishCodeCoverage(summaryFilePath, data);
}

private void WriteFormatCommand(string actionName, string value)
{
_writer.Write("{0}{1}{2}{3}", FormatPrefix, actionName, MessagePostfix, value);
}

private void WriteLoggingCommand(string actionName, string value)
{
WriteLoggingCommand(actionName, new Dictionary<string, string>(), value);
_writer.Write("{0}{1}{2}{3}", MessagePrefix, actionName, MessagePostfix, value);
}

private void WriteLoggingCommand(string actionName, Dictionary<string, string> properties, string value)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System;

namespace Cake.Common.Build.AzurePipelines
{
/// <summary>
/// A set of extensions for allowing "using" with Azure Pipelines "blocks".
/// </summary>
public static class AzurePipelinesDisposableExtensions
{
/// <summary>
/// Groups Azure Pipelines output.
/// </summary>
/// <param name="azurePipelinesCommands">The Azure Pipelines Commands.</param>
/// <param name="name">The name.</param>
/// <returns>An <see cref="IDisposable"/>.</returns>
public static IDisposable Group(this IAzurePipelinesCommands azurePipelinesCommands, string name)
{
ArgumentNullException.ThrowIfNull(name);

azurePipelinesCommands.BeginGroup(name);
return new AzurePipelinesActionDisposable<IAzurePipelinesCommands>(azurePipelinesCommands, apc => apc.EndGroup());
}

/// <summary>
/// Disposable helper for writing Azure Pipelines message blocks.
/// </summary>
internal sealed class AzurePipelinesActionDisposable<T> : IDisposable
{
private readonly Action<T> _disposeAction;
private readonly T _instance;

/// <summary>
/// Initializes a new instance of the <see cref="AzurePipelinesActionDisposable{T}"/> class.
/// </summary>
/// <param name="instance">The instance.</param>
/// <param name="disposeAction">The dispose action.</param>
public AzurePipelinesActionDisposable(T instance, Action<T> disposeAction)
{
_instance = instance;
_disposeAction = disposeAction;
}

/// <summary>
/// Calls dispose action.
/// </summary>
public void Dispose()
{
_disposeAction(_instance);
}
}
}
}
17 changes: 17 additions & 0 deletions src/Cake.Common/Build/AzurePipelines/IAzurePipelinesCommands.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,23 @@ public interface IAzurePipelinesCommands
/// <param name="data">The message data.</param>
void WriteError(string message, AzurePipelinesMessageData data);

/// <summary>
/// Begin a collapsible group.
/// </summary>
/// <param name="name">The name of the group.</param>
public void BeginGroup(string name);

/// <summary>
/// End a collapsible group.
/// </summary>
public void EndGroup();

/// <summary>
/// Log section.
/// </summary>
/// <param name="name">The name of the section.</param>
public void Section(string name);

/// <summary>
/// Set progress and current operation for current task.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,6 @@
<RunWorkingDirectory>$(MSBuildProjectDirectory)</RunWorkingDirectory>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Cake.Frosting" Version="3.0.0" />
<PackageReference Include="Cake.Frosting" Version="3.1.0.0" />
</ItemGroup>
</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
Task("Cake.Common.Build.AzurePipelinesProvider.Commands.Group")
.Does(() => {
using (AzurePipelines.Commands.Group("Cake Group"))
{
Console.WriteLine("This is inside a group");
}
});

Task("Cake.Common.Build.AzurePipelinesProvider.Commands.Section")
.Does(() => {
AzurePipelines.Commands.Section("Cake Section");
});


var azurePipelinesProviderTask = Task("Cake.Common.Build.AzurePipelinesProvider");

if(AzurePipelines.IsRunningOnAzurePipelines)
{
azurePipelinesProviderTask
.IsDependentOn("Cake.Common.Build.AzurePipelinesProvider.Commands.Group")
.IsDependentOn("Cake.Common.Build.AzurePipelinesProvider.Commands.Section");
}
4 changes: 3 additions & 1 deletion tests/integration/Cake.Common/Build/BuildSystemAliases.cake
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#load "GitHubActions/GitHubActionsProvider.cake"
#load "AzurePipelines/AzurePipelinesProvider.cake"

Task("Cake.Common.Build.BuildSystemAliases.BuildProvider")
.DoesForEach(
Expand All @@ -10,4 +11,5 @@ Task("Cake.Common.Build.BuildSystemAliases.BuildProvider")

Task("Cake.Common.Build.BuildSystemAliases")
.IsDependentOn("Cake.Common.Build.BuildSystemAliases.BuildProvider")
.IsDependentOn("Cake.Common.Build.GitHubActionsProvider");
.IsDependentOn("Cake.Common.Build.GitHubActionsProvider")
.IsDependentOn("Cake.Common.Build.AzurePipelinesProvider");