diff --git a/.github/workflows/maestro-changelog.yml b/.github/workflows/maestro-changelog.yml index e8a308e390c9..90b51961df75 100644 --- a/.github/workflows/maestro-changelog.yml +++ b/.github/workflows/maestro-changelog.yml @@ -17,17 +17,17 @@ jobs: if: github.event.pull_request.user.login == 'dotnet-maestro[bot]' steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false - uses: actions/setup-dotnet@a98b56852c35b8e3190ac28c8c2271da59106c68 # v6.0.0 with: - dotnet-version: '9' + global-json-file: ./global.json - name: 'Compute changelog' run: | set -exo pipefail - git clone https://github.com/spouliot/dotnet-tools - cd dotnet-tools/changelog - dotnet build - ./bin/Debug/net9.0/changelog https://github.com/$GITHUB_REPOSITORY/pull/${GITHUB_REF_NAME/\/*/} > /tmp/changelog.txt 2>&1 + dotnet run --project scripts/changelog/changelog.csproj --verbosity quiet -- https://github.com/$GITHUB_REPOSITORY/pull/${GITHUB_REF_NAME/\/*/} /tmp/changelog.txt - name: 'Add changelog' uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 @@ -45,7 +45,9 @@ jobs: issue_number: context.issue.number }).then ((comments) => { - const changelogComment = comments.find(comment => comment.body.includes (".net ChangeLog for") && comment.user.login == 'github-actions[bot]') + const changelogComment = comments.find(comment => + (comment.body.includes (".NET Changelog for") || comment.body.includes (".net ChangeLog for")) && + comment.user.login == 'github-actions[bot]') if (changelogComment) commentId = changelogComment.id }) diff --git a/scripts/changelog/Program.cs b/scripts/changelog/Program.cs new file mode 100644 index 000000000000..c36b3539ddbf --- /dev/null +++ b/scripts/changelog/Program.cs @@ -0,0 +1,141 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; +using System.IO; +using System.Net.Http; +using System.Threading.Tasks; + +namespace changelog { + + class Program { + static readonly List list = new (); + static readonly List filters = new (); + + // current repo (1) points to dotnet/installer (2) + // getting into other repos (pointed by dotnet/installer) can show different results + static int level = 2; + + static async Task Main (string [] args) + { + if (args.Length < 2) { + Console.Error.WriteLine ("Usage: changelog [repo-filter ...]"); + Environment.ExitCode = 1; + return; + } + + var pr = args [0]; + var outputFile = args [1]; + for (int i = 2; i < args.Length; i++) + filters.Add (args [i]); + + using (var writer = new StreamWriter (outputFile)) { + writer.WriteLine ($"# .NET Changelog for {pr}"); + if (TryGetGitHubPullRequestDiffUrl (pr, out var diffUrl)) + pr = diffUrl; + list.Add (pr); + await Process (writer); + writer.WriteLine ("Generated using scripts/changelog"); + } + } + + static bool TryGetGitHubPullRequestDiffUrl (string url, out string diffUrl) + { + diffUrl = url; + if (!Uri.TryCreate (url, UriKind.Absolute, out var uri)) + return false; + if (uri.Host != "github.com") + return false; + if (url.EndsWith (".diff", StringComparison.Ordinal)) + return false; + + var parts = uri.AbsolutePath.Split ('/', StringSplitOptions.RemoveEmptyEntries); + if (parts.Length != 4 || parts [2] != "pull") + return false; + + diffUrl = $"https://patch-diff.githubusercontent.com/raw/{string.Join ('/', parts)}.diff"; + return true; + } + + static async Task Process (TextWriter writer) + { + using var client = new HttpClient (); + for (int i = 0; i < Math.Min (list.Count, level); i++) { + writer.WriteLine ($"## Level {i + 1}"); + var url = list [i]; + if (url.StartsWith ("https://", StringComparison.Ordinal)) { + using var result = await client.GetAsync (list [i]); + result.EnsureSuccessStatusCode (); + using var stream = await result.Content.ReadAsStreamAsync (); + ProcessDiff (stream, writer); + } else { + using var stream = new FileStream (url, FileMode.Open, FileAccess.Read); + ProcessDiff (stream, writer); + } + writer.WriteLine (); + } + } + + static bool Include (string uri) + { + if (filters.Count == 0) + return true; + + foreach (var filter in filters) { + if (uri.EndsWith (filter, StringComparison.Ordinal)) + return true; + } + return false; + } + + static void ProcessDiff (Stream s, TextWriter writer) + { + bool processing = false; + var uri = ""; + var old_sha = ""; + var new_sha = ""; + using (var sr = new StreamReader (s)) { + while (!sr.EndOfStream) { + var line = sr.ReadLine (); + if (line is null) + break; + if (line == "diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml") { + processing = true; + continue; + } + if (processing) { + if (line.StartsWith ("diff --git ", StringComparison.Ordinal)) + return; + if (line.Length < 1) + continue; + bool removal = (line [0] == '-'); + bool addition = (line [0] == '+'); + var tl = (removal || addition) ? line [1..] : line; + tl = tl.Trim (); + if (tl.StartsWith ("", StringComparison.Ordinal)) { + uri = tl [5..^6]; + old_sha = ""; + new_sha = ""; + } else if (removal && tl.StartsWith ("", StringComparison.Ordinal)) { + old_sha = tl [5..^6]; + } else if (addition && tl.StartsWith ("", StringComparison.Ordinal)) { + new_sha = tl [5..^6]; + if (!Include (uri)) + continue; + if (string.IsNullOrEmpty (old_sha)) { + writer.WriteLine ($"* {uri} [{new_sha [0..7]}]({uri}/commits/{new_sha}) (new dependency)"); + } else { + var diff_url = $"{uri}/compare/{old_sha}...{new_sha}.diff"; + writer.WriteLine ($"* {uri} [{old_sha [0..7]}...{new_sha [0..7]}]({uri}/compare/{old_sha}...{new_sha})"); + // skip duplicates (if same revisions are used) + if (!list.Contains (diff_url)) + list.Add (diff_url); + } + } + } + } + } + } + } +} diff --git a/scripts/changelog/README.md b/scripts/changelog/README.md new file mode 100644 index 000000000000..599edc954696 --- /dev/null +++ b/scripts/changelog/README.md @@ -0,0 +1,16 @@ + +# Changelog + +This tool reads dependency changes from a pull request and formats them as a +Markdown changelog. + +## Usage + +```sh +dotnet run --project scripts/changelog/changelog.csproj -- https://github.com/dotnet/macios/pull/11175 /tmp/changelog.txt +``` + +Additional arguments filter the dependency repositories included in the output. diff --git a/scripts/changelog/changelog.csproj b/scripts/changelog/changelog.csproj new file mode 100644 index 000000000000..bd08a2be8d8d --- /dev/null +++ b/scripts/changelog/changelog.csproj @@ -0,0 +1,9 @@ + + + + net$(BundledNETCoreAppTargetFrameworkVersion) + + diff --git a/scripts/changelog/fragment.mk b/scripts/changelog/fragment.mk new file mode 100644 index 000000000000..e07f8990a9c8 --- /dev/null +++ b/scripts/changelog/fragment.mk @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. +include $(TOP)/scripts/template.mk +$(eval $(call TemplateScript,CHANGELOG,changelog))