From dfcbbbe7c0cf864f3aad2502254748a4788b051f Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Thu, 23 Jan 2025 15:35:27 -0500 Subject: [PATCH 1/6] initial code Make the initial code to create a check run using a chained workflow. --- .github/workflows/grammar-validator.yaml | 4 +- .github/workflows/renumber-sections.yaml | 21 +++++++++- .github/workflows/report-status.yml | 26 ++++++++++++ .github/workflows/smart-quotes.yaml | 4 +- .github/workflows/test-examples.yaml | 4 +- .github/workflows/tools-tests.yaml | 4 +- .github/workflows/update-on-merge.yaml | 4 +- .github/workflows/word-converter.yaml | 4 +- tools/ExampleTester/ExampleTester.csproj | 4 +- .../MarkdownConverter.Tests.csproj | 2 +- .../MarkdownConverter.csproj | 2 +- tools/StandardAnchorTags/Program.cs | 2 +- .../StandardAnchorTags.csproj | 4 +- tools/Utilities/CustomSerializer.cs | 12 ++++++ tools/Utilities/StatusCheckLogger.cs | 41 ++++++++++++++++++- tools/Utilities/Utilities.csproj | 6 ++- tools/run-section-renumber.sh | 3 +- 17 files changed, 122 insertions(+), 25 deletions(-) create mode 100644 .github/workflows/report-status.yml create mode 100644 tools/Utilities/CustomSerializer.cs diff --git a/.github/workflows/grammar-validator.yaml b/.github/workflows/grammar-validator.yaml index 1e86eac61..05e4c1d5c 100644 --- a/.github/workflows/grammar-validator.yaml +++ b/.github/workflows/grammar-validator.yaml @@ -21,10 +21,10 @@ jobs: - name: Check out our repo uses: actions/checkout@v2 - - name: Setup .NET 8.0 + - name: Setup .NET 9.0 uses: actions/setup-dotnet@v1 with: - dotnet-version: 8.0.x + dotnet-version: 9.0.x - name: Set up JDK 15 uses: actions/setup-java@v2 diff --git a/.github/workflows/renumber-sections.yaml b/.github/workflows/renumber-sections.yaml index 13f4ecba7..3756d601a 100644 --- a/.github/workflows/renumber-sections.yaml +++ b/.github/workflows/renumber-sections.yaml @@ -29,9 +29,28 @@ jobs: - name: Setup .NET 8.0 uses: actions/setup-dotnet@v1 with: - dotnet-version: 8.0.x + dotnet-version: 9.0.x - name: Run section renumbering dry run + id: section-renumber run: | cd tools ./run-section-renumber.sh + + - name: Trigger Workflow + uses: actions/github-script@v6 + with: + script: | + github.rest.actions.createWorkflowDispatch({ + owner: context.repo.owner, + repo: context.repo.repo, + workflow_id: 'report-status.yml', + ref: '${{ github.head_ref }}', + inputs: { + "head_sha": '${{ steps.section-renumber.output.head_sha }}', + "name": '${{ steps.section-renumber.output.check_name }}', + "conclusion": '${{ steps.section-renumber.output.conclusion }}', + "summary": '${{ steps.section-renumber.output.summary }}', + "annotations": '${{ steps.section-renumber.output.annotations }}' + } + }) diff --git a/.github/workflows/report-status.yml b/.github/workflows/report-status.yml new file mode 100644 index 000000000..63b71b144 --- /dev/null +++ b/.github/workflows/report-status.yml @@ -0,0 +1,26 @@ +name: Report status + +# Triggers the workflow when a workflow that generates status completes +on: + workflow_run: + workflows: ["Renumber standard TOC"] + types: + - completed + +jobs: + report-status: + runs-on: ubuntu-latest + permissions: + checks: write + pull-requests: write + steps: + - uses: LouisBrunner/checks-action@v2.0.0 + with: + token: ${{ secrets.GITHUB_TOKEN }} + sha: ${{ inputs.head_sha }} + name: ${{ inputs.check_name }} + conclusion: ${{ inputs.conclusion}} + # output.summary is required with actions! + output: | + {"summary":"${{inputs.summary}}"} + annotations: ${{ inputs.annotations }} diff --git a/.github/workflows/smart-quotes.yaml b/.github/workflows/smart-quotes.yaml index 551084179..f2746bf63 100644 --- a/.github/workflows/smart-quotes.yaml +++ b/.github/workflows/smart-quotes.yaml @@ -24,10 +24,10 @@ jobs: - name: Check out our repo uses: actions/checkout@v2 - - name: Setup .NET 8.0 + - name: Setup .NET 9.0 uses: actions/setup-dotnet@v1 with: - dotnet-version: 8.0.x + dotnet-version: 9.0.x - name: Smarten quotes id: smarten-quote diff --git a/.github/workflows/test-examples.yaml b/.github/workflows/test-examples.yaml index 727e996fb..f5f20d3f7 100644 --- a/.github/workflows/test-examples.yaml +++ b/.github/workflows/test-examples.yaml @@ -32,12 +32,12 @@ jobs: # 8.0 for the tools themselves. (The closer we # are to the target language version we're standardising, # the better.) - - name: Setup .NET 6.0 and 8.0 + - name: Setup .NET 6.0 and 9.0 uses: actions/setup-dotnet@v3 with: dotnet-version: | 6.0.x - 8.0.x + 9.0.x - name: Extract and validate tests run: | diff --git a/.github/workflows/tools-tests.yaml b/.github/workflows/tools-tests.yaml index ff3790536..0ee087ebf 100644 --- a/.github/workflows/tools-tests.yaml +++ b/.github/workflows/tools-tests.yaml @@ -25,10 +25,10 @@ jobs: - name: Check out our repo uses: actions/checkout@v2 - - name: Setup .NET 8.0 + - name: Setup .NET 9.0 uses: actions/setup-dotnet@v1 with: - dotnet-version: 8.0.x + dotnet-version: 9.0.x - name: Run all tests run: | diff --git a/.github/workflows/update-on-merge.yaml b/.github/workflows/update-on-merge.yaml index 56fe69db8..1cc7a277f 100644 --- a/.github/workflows/update-on-merge.yaml +++ b/.github/workflows/update-on-merge.yaml @@ -30,10 +30,10 @@ jobs: - name: Check out our repo uses: actions/checkout@v2 - - name: Setup .NET 8.0 + - name: Setup .NET 9.0 uses: actions/setup-dotnet@v1 with: - dotnet-version: 8.0.x + dotnet-version: 9.0.x - name: Set up JDK 15 uses: actions/setup-java@v1 diff --git a/.github/workflows/word-converter.yaml b/.github/workflows/word-converter.yaml index 696f998fd..939e47160 100644 --- a/.github/workflows/word-converter.yaml +++ b/.github/workflows/word-converter.yaml @@ -26,10 +26,10 @@ jobs: - name: Check out our repo uses: actions/checkout@v2 - - name: Setup .NET 8.0 + - name: Setup .NET 9.0 uses: actions/setup-dotnet@v1 with: - dotnet-version: 8.0.x + dotnet-version: 9.0.x - name: Run converter run: | diff --git a/tools/ExampleTester/ExampleTester.csproj b/tools/ExampleTester/ExampleTester.csproj index a6923d2bd..7b1e485a6 100644 --- a/tools/ExampleTester/ExampleTester.csproj +++ b/tools/ExampleTester/ExampleTester.csproj @@ -1,8 +1,8 @@ - + Exe - net8.0 + net9.0 enable enable diff --git a/tools/MarkdownConverter.Tests/MarkdownConverter.Tests.csproj b/tools/MarkdownConverter.Tests/MarkdownConverter.Tests.csproj index 2e30c08b4..8390ef41c 100644 --- a/tools/MarkdownConverter.Tests/MarkdownConverter.Tests.csproj +++ b/tools/MarkdownConverter.Tests/MarkdownConverter.Tests.csproj @@ -1,7 +1,7 @@  - net8.0 + net9.0 enable enable false diff --git a/tools/MarkdownConverter/MarkdownConverter.csproj b/tools/MarkdownConverter/MarkdownConverter.csproj index eb0bc9534..cc1056e1d 100644 --- a/tools/MarkdownConverter/MarkdownConverter.csproj +++ b/tools/MarkdownConverter/MarkdownConverter.csproj @@ -2,7 +2,7 @@ Exe - net8.0 + net9.0 enable enable NU1701 diff --git a/tools/StandardAnchorTags/Program.cs b/tools/StandardAnchorTags/Program.cs index 6c0f77449..89b646b25 100644 --- a/tools/StandardAnchorTags/Program.cs +++ b/tools/StandardAnchorTags/Program.cs @@ -61,7 +61,7 @@ static async Task Main(string owner, string repo, bool dryrun =false) { if ((token is not null) && (headSha is not null)) { - await logger.BuildCheckRunResult(token, owner, repo, headSha); + var annotations = await logger.BuildCheckRunResult(token, owner, repo, headSha); } } return logger.Success ? 0 : 1; diff --git a/tools/StandardAnchorTags/StandardAnchorTags.csproj b/tools/StandardAnchorTags/StandardAnchorTags.csproj index 8157f79fc..ddbfffd35 100644 --- a/tools/StandardAnchorTags/StandardAnchorTags.csproj +++ b/tools/StandardAnchorTags/StandardAnchorTags.csproj @@ -1,8 +1,8 @@ - + Exe - net8.0 + net9.0 enable enable diff --git a/tools/Utilities/CustomSerializer.cs b/tools/Utilities/CustomSerializer.cs new file mode 100644 index 000000000..2d2705ea6 --- /dev/null +++ b/tools/Utilities/CustomSerializer.cs @@ -0,0 +1,12 @@ +using System.Text.Json.Serialization; +using Octokit; + +namespace Utilities; + +// This defines the custom serializer for the annotations block. +// That enables the status checks to write the annotations to +// the GitHub Actions output. That lets a subsequent action read +// these annotations in a different security context and write +// them to the PR. +[JsonSerializable(typeof(IList))] +public sealed partial class JsonCheckRunAnnotationSerializerContext : JsonSerializerContext; diff --git a/tools/Utilities/StatusCheckLogger.cs b/tools/Utilities/StatusCheckLogger.cs index 2feaeb65c..1c5edd99b 100644 --- a/tools/Utilities/StatusCheckLogger.cs +++ b/tools/Utilities/StatusCheckLogger.cs @@ -1,4 +1,7 @@ using Octokit; +using Actions.Core.Extensions; +using Actions.Core.Services; +using Microsoft.Extensions.DependencyInjection; namespace Utilities; @@ -24,6 +27,15 @@ public record StatusCheckMessage(string file, int StartLine, int EndLine, string /// The name of the tool that is running the check public class StatusCheckLogger(string pathToRoot, string toolName) { + private static ICoreService? gitHubCoreService; + public static void Initializer() + { + using var provider = new ServiceCollection() + .AddGitHubActionsCore() + .BuildServiceProvider(); + gitHubCoreService = provider.GetRequiredService(); + } + private List annotations = []; public bool Success { get; private set; } = true; @@ -153,13 +165,17 @@ public void ExitOnFailure(StatusCheckMessage d) /// The GitHub repo name /// The head sha when running as a GitHub action /// The full check run result object - public async Task BuildCheckRunResult(string token, string owner, string repo, string sha) + public async Task> BuildCheckRunResult(string token, string owner, string repo, string sha) { + + var title = $"{toolName} Check Run results"; + var summary = $"{toolName} result is {(Success ? "success" : "failure")} with {annotations.Count} diagnostics."; + NewCheckRun result = new(toolName, sha) { Status = CheckStatus.Completed, Conclusion = Success ? CheckConclusion.Success : CheckConclusion.Failure, - Output = new($"{toolName} Check Run results", $"{toolName} result is {(Success ? "success" : "failure")} with {annotations.Count} diagnostics.") + Output = new(title, summary) { Annotations = annotations } @@ -173,6 +189,26 @@ public async Task BuildCheckRunResult(string token, string owner, string repo, s try { await client.Check.Run.Create(owner, repo, result); + var asyncTask = gitHubCoreService?.SetOutputAsync("check_name", title, JsonCheckRunAnnotationSerializerContext.Default.String) + ?? ValueTask.CompletedTask; + await asyncTask; + + asyncTask = gitHubCoreService?.SetOutputAsync("conclusion", Success ? "success" : "failure", JsonCheckRunAnnotationSerializerContext.Default.String) + ?? ValueTask.CompletedTask; + await asyncTask; + + asyncTask = gitHubCoreService?.SetOutputAsync("summary", summary, JsonCheckRunAnnotationSerializerContext.Default.String) + ?? ValueTask.CompletedTask; + await asyncTask; + + asyncTask = gitHubCoreService?.SetOutputAsync("head_sha", sha, JsonCheckRunAnnotationSerializerContext.Default.String) + ?? ValueTask.CompletedTask; + await asyncTask; + + asyncTask = gitHubCoreService?.SetOutputAsync("annotations", annotations, JsonCheckRunAnnotationSerializerContext.Default.IListNewCheckRunAnnotation) + ?? ValueTask.CompletedTask; + await asyncTask; + // Now, we have a file named annotations. } // If the token does not have the correct permissions, we will get a 403 // Once running on a branch on the dotnet org, this should work correctly. @@ -182,5 +218,6 @@ public async Task BuildCheckRunResult(string token, string owner, string repo, s Console.WriteLine("Exception details:"); Console.WriteLine(e); } + return annotations; } } diff --git a/tools/Utilities/Utilities.csproj b/tools/Utilities/Utilities.csproj index b04c9d095..d811a0f28 100644 --- a/tools/Utilities/Utilities.csproj +++ b/tools/Utilities/Utilities.csproj @@ -1,12 +1,14 @@ - + - net8.0 + net9.0 enable enable + + diff --git a/tools/run-section-renumber.sh b/tools/run-section-renumber.sh index bb9b38584..e22e22b61 100755 --- a/tools/run-section-renumber.sh +++ b/tools/run-section-renumber.sh @@ -8,5 +8,6 @@ dotnet run --project $PROJECT -- --owner dotnet --repo csharpstandard if [ -n "$GITHUB_OUTPUT" ] then - echo "status=success" >> $GITHUB_OUTPUT + echo "status=success" >> $GITHUB_OUTPUT + # blob is in the GITHUB_OUTPUT fi From 6389fb281c70174d1809116e77a79a63668c00a5 Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Thu, 23 Jan 2025 15:52:40 -0500 Subject: [PATCH 2/6] next try --- .github/workflows/grammar-validator.yaml | 4 ++-- .github/workflows/renumber-sections.yaml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/grammar-validator.yaml b/.github/workflows/grammar-validator.yaml index 05e4c1d5c..1e86eac61 100644 --- a/.github/workflows/grammar-validator.yaml +++ b/.github/workflows/grammar-validator.yaml @@ -21,10 +21,10 @@ jobs: - name: Check out our repo uses: actions/checkout@v2 - - name: Setup .NET 9.0 + - name: Setup .NET 8.0 uses: actions/setup-dotnet@v1 with: - dotnet-version: 9.0.x + dotnet-version: 8.0.x - name: Set up JDK 15 uses: actions/setup-java@v2 diff --git a/.github/workflows/renumber-sections.yaml b/.github/workflows/renumber-sections.yaml index 3756d601a..e05b704ac 100644 --- a/.github/workflows/renumber-sections.yaml +++ b/.github/workflows/renumber-sections.yaml @@ -42,8 +42,8 @@ jobs: with: script: | github.rest.actions.createWorkflowDispatch({ - owner: context.repo.owner, - repo: context.repo.repo, + owner: "dotnet", + repo: "csharpstandard", workflow_id: 'report-status.yml', ref: '${{ github.head_ref }}', inputs: { From 15188589a2123bd263adb01a9532feb29fd746d5 Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Thu, 23 Jan 2025 15:56:46 -0500 Subject: [PATCH 3/6] ooops --- tools/Utilities/StatusCheckLogger.cs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/tools/Utilities/StatusCheckLogger.cs b/tools/Utilities/StatusCheckLogger.cs index 1c5edd99b..f3839d1ea 100644 --- a/tools/Utilities/StatusCheckLogger.cs +++ b/tools/Utilities/StatusCheckLogger.cs @@ -189,6 +189,17 @@ public async Task> BuildCheckRunResult(string token try { await client.Check.Run.Create(owner, repo, result); + } + // If the token does not have the correct permissions, we will get a 403 + // Once running on a branch on the dotnet org, this should work correctly. + catch (ForbiddenException e) + { + Console.WriteLine("===== WARNING: Could not create a check run.====="); + Console.WriteLine("Exception details:"); + Console.WriteLine(e); + } + try + { var asyncTask = gitHubCoreService?.SetOutputAsync("check_name", title, JsonCheckRunAnnotationSerializerContext.Default.String) ?? ValueTask.CompletedTask; await asyncTask; From 9b9881de8e1718cc40ae4dea4b081cd0d4a81e24 Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Fri, 24 Jan 2025 10:30:17 -0500 Subject: [PATCH 4/6] Apply suggestions from code review Co-authored-by: David Pine --- tools/Utilities/StatusCheckLogger.cs | 43 ++++++++++++++++------------ 1 file changed, 25 insertions(+), 18 deletions(-) diff --git a/tools/Utilities/StatusCheckLogger.cs b/tools/Utilities/StatusCheckLogger.cs index f3839d1ea..547ce8044 100644 --- a/tools/Utilities/StatusCheckLogger.cs +++ b/tools/Utilities/StatusCheckLogger.cs @@ -27,13 +27,15 @@ public record StatusCheckMessage(string file, int StartLine, int EndLine, string /// The name of the tool that is running the check public class StatusCheckLogger(string pathToRoot, string toolName) { - private static ICoreService? gitHubCoreService; - public static void Initializer() + private Lazy _gitHubCoreService = new(() => InitializeCoreService()); + + private static ICoreService InitializeCoreService() { using var provider = new ServiceCollection() .AddGitHubActionsCore() .BuildServiceProvider(); - gitHubCoreService = provider.GetRequiredService(); + + return provider.GetRequiredService(); } private List annotations = []; @@ -200,25 +202,30 @@ public async Task> BuildCheckRunResult(string token } try { - var asyncTask = gitHubCoreService?.SetOutputAsync("check_name", title, JsonCheckRunAnnotationSerializerContext.Default.String) - ?? ValueTask.CompletedTask; - await asyncTask; + var core = _gitHubCoreService.Value; + + await core.GroupAsync("Writing run outputs", async () => + { + await core.SetOutputAsync("check_name", title, JsonCheckRunAnnotationSerializerContext.Default.String); + + core.WriteInfo("Set check_name output"); + + await core.SetOutputAsync("conclusion", Success ? "success" : "failure", JsonCheckRunAnnotationSerializerContext.Default.String); + + core.WriteInfo("Set conclusion output"); + + await core.SetOutputAsync("summary", summary, JsonCheckRunAnnotationSerializerContext.Default.String); - asyncTask = gitHubCoreService?.SetOutputAsync("conclusion", Success ? "success" : "failure", JsonCheckRunAnnotationSerializerContext.Default.String) - ?? ValueTask.CompletedTask; - await asyncTask; + core.WriteInfo("Set summary output"); - asyncTask = gitHubCoreService?.SetOutputAsync("summary", summary, JsonCheckRunAnnotationSerializerContext.Default.String) - ?? ValueTask.CompletedTask; - await asyncTask; + await core.SetOutputAsync("head_sha", sha, JsonCheckRunAnnotationSerializerContext.Default.String); - asyncTask = gitHubCoreService?.SetOutputAsync("head_sha", sha, JsonCheckRunAnnotationSerializerContext.Default.String) - ?? ValueTask.CompletedTask; - await asyncTask; + core.WriteInfo("Set head_sha output"); - asyncTask = gitHubCoreService?.SetOutputAsync("annotations", annotations, JsonCheckRunAnnotationSerializerContext.Default.IListNewCheckRunAnnotation) - ?? ValueTask.CompletedTask; - await asyncTask; + await core.SetOutputAsync("annotations", annotations, JsonCheckRunAnnotationSerializerContext.Default.IListNewCheckRunAnnotation); + + core.WriteInfo("Set annotations output"); + }); // Now, we have a file named annotations. } // If the token does not have the correct permissions, we will get a 403 From d7a2bd8d3b5c7341bd093750e91c1653748febab Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Fri, 24 Jan 2025 10:51:48 -0500 Subject: [PATCH 5/6] compiler issue --- tools/Utilities/StatusCheckLogger.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/Utilities/StatusCheckLogger.cs b/tools/Utilities/StatusCheckLogger.cs index 547ce8044..73c7d90b2 100644 --- a/tools/Utilities/StatusCheckLogger.cs +++ b/tools/Utilities/StatusCheckLogger.cs @@ -225,6 +225,7 @@ await core.GroupAsync("Writing run outputs", async () => await core.SetOutputAsync("annotations", annotations, JsonCheckRunAnnotationSerializerContext.Default.IListNewCheckRunAnnotation); core.WriteInfo("Set annotations output"); + return true; // to get a natural lambda type. }); // Now, we have a file named annotations. } From 16c717c090988ecab3425857d19ddefeae388481 Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Fri, 24 Jan 2025 12:15:44 -0500 Subject: [PATCH 6/6] Update .github/workflows/renumber-sections.yaml Co-authored-by: David Pine --- .github/workflows/renumber-sections.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/renumber-sections.yaml b/.github/workflows/renumber-sections.yaml index e05b704ac..41bd91418 100644 --- a/.github/workflows/renumber-sections.yaml +++ b/.github/workflows/renumber-sections.yaml @@ -38,7 +38,7 @@ jobs: ./run-section-renumber.sh - name: Trigger Workflow - uses: actions/github-script@v6 + uses: actions/github-script@v7 with: script: | github.rest.actions.createWorkflowDispatch({