Skip to content

Commit 61ef9b3

Browse files
authored
Publish release (#3022)
* Adding icon diff functionality * Updating pipelines to use updated process * Fixing shell * Setting shell defaults * Moving mdresgento window agent * Removing working directory assumption * Fixing directory path * Set working directory for download * Fixing nuget ordering * Fixing artifact name * Setting artifact name * Updating upload artifact version Fixing pipeline warnings * Renaming the icon updates file * Fixing another warning for the action
1 parent e367b42 commit 61ef9b3

File tree

8 files changed

+647
-408
lines changed

8 files changed

+647
-408
lines changed

.github/actions/build-and-test/action.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ runs:
3838

3939
- name: Upload Screenshots
4040
if: ${{ always() }}
41-
uses: actions/upload-artifact@v2
41+
uses: actions/upload-artifact@v3
4242
with:
4343
name: Screenshots-${{ github.run_number }}
4444
path: ${{ github.workspace }}/MaterialDesignThemes.UITests/bin/${{ inputs.buildConfiguration }}/net7.0-windows/Screenshots

.github/workflows/prepare_release.yml

Lines changed: 43 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,15 @@ env:
1717
# Needed for GitHub CLI
1818
GH_TOKEN: ${{ github.token }}
1919

20+
defaults:
21+
run:
22+
shell: pwsh
23+
2024
jobs:
2125
collect_contributors:
2226
runs-on: ubuntu-latest
2327
name: Generate Release Notes
28+
2429
steps:
2530
- name: Get Contributors
2631
id: get-contribs
@@ -32,7 +37,6 @@ jobs:
3237
token: ${{ github.token }}
3338

3439
- name: Generate Release Notes
35-
shell: pwsh
3640
run: |
3741
$response = gh api --method POST -H "Accept: application/vnd.github+json" /repos/${{ github.repository }}/releases/generate-notes -f tag_name='v${{ inputs.milestone }}'
3842
$json = $response | ConvertFrom-Json
@@ -44,7 +48,7 @@ jobs:
4448
cat "ReleaseNotes.md"
4549
4650
- name: Upload Release Notes
47-
uses: actions/upload-artifact@v2
51+
uses: actions/upload-artifact@v3
4852
with:
4953
name: ReleaseNotes
5054
path: "ReleaseNotes.md"
@@ -67,23 +71,55 @@ jobs:
6771
nugetKey: ${{ secrets.PAT }}
6872

6973
- name: Build NuGets
70-
shell: pwsh
7174
run: .\Scripts\BuildNugets.ps1 -MDIXVersion ${{ env.mdixVersion }} -MDIXMahAppsVersion ${{ env.mdixMahAppsVersion }} -MDIXColorsVersion ${{ env.mdixColorsVersion }}
7275

7376
- name: Upload NuGets
74-
uses: actions/upload-artifact@v2
77+
uses: actions/upload-artifact@v3
7578
with:
7679
name: NuGets
7780
path: "*.nupkg"
7881

7982
- name: Upload Demo App
80-
uses: actions/upload-artifact@v2
83+
uses: actions/upload-artifact@v3
8184
with:
8285
name: DemoApp
8386
path: "MainDemo.Wpf/bin/${{ env.buildConfiguration }}"
8487

88+
build_icon_changes:
89+
needs: [build_artifacts]
90+
runs-on: windows-latest
91+
name: Get Icon Changes
92+
93+
steps:
94+
- uses: actions/checkout@v3
95+
with:
96+
fetch-depth: 0
97+
98+
- name: Download NuGet Artifacts
99+
uses: actions/download-artifact@v3
100+
with:
101+
name: NuGets
102+
path: nugets
103+
104+
- name: Get Previous NuGet release
105+
run: |
106+
$release = (gh api -H "Accept: application/vnd.github+json" /repos/${{ github.repository }}/releases/latest) | ConvertFrom-Json
107+
gh release download "$($release.tag_name)" --repo "${{ github.repository }}" --pattern *.nupkg
108+
working-directory: nugets
109+
110+
- name: Create Icon Diff File
111+
run: |
112+
dotnet run --project ./mdresgen/mdresgen.csproj -c Release -- icon-diff
113+
114+
- name: Upload Icon Changes
115+
uses: actions/upload-artifact@v3
116+
with:
117+
name: IconChanges
118+
path: "IconChanges*.md"
119+
120+
85121
create_release:
86-
needs: [collect_contributors, build_artifacts]
122+
needs: [collect_contributors, build_artifacts, build_icon_changes]
87123
runs-on: ubuntu-latest
88124
name: Update Release
89125

@@ -109,7 +145,6 @@ jobs:
109145
name: ReleaseNotes
110146

111147
- name: Create Release
112-
shell: pwsh
113148
run: |
114149
# We can't use glob pattern because of this bug https://github.com/cli/cli/issues/5099
115-
gh release create v${{ inputs.milestone }} --repo '${{ github.repository }}' --draft --latest --title "${{ inputs.milestone }}" --notes-file ReleaseNotes.md (Get-Item '${{ github.workspace }}/nugets/*.nupkg') '${{ github.workspace }}/DemoApp.zip'
150+
gh release create v${{ inputs.milestone }} --repo '${{ github.repository }}' --draft --latest --title "${{ inputs.milestone }}" --notes-file ReleaseNotes.md (Get-Item '${{ github.workspace }}/nugets/*.nupkg') '${{ github.workspace }}/DemoApp.zip'

.github/workflows/publish_release.yml

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,22 +4,20 @@ on:
44
release:
55
types: [published]
66

7+
defaults:
8+
run:
9+
shell: pwsh
10+
711
jobs:
812
publish_nugets:
913
runs-on: ubuntu-latest
1014
name: Publish NuGets
1115

1216
steps:
1317
- name: Download Release Artifacts
14-
shell: pwsh
1518
run: |
16-
$release = (gh api -H "Accept: application/vnd.github+json" /repos/${{ github.repository }}/releases/tags/github.ref_name) | ConvertFrom-Json
17-
18-
foreach($asset in $release.assets) {
19-
if ($asset.name.EndsWith(".nupkg")){
20-
Write-Host "Downloading $($asset.id) - $($asset.name)"
21-
gh api -H "Accept: application/octet-stream" "/repos/${{ github.repository }}/releases/assets/$($asset.id)" | Out-File $asset.name
22-
}
23-
}
19+
$release = (gh api -H "Accept: application/vnd.github+json" /repos/${{ github.repository }}/releases/tags/${{ github.ref_name }}) | ConvertFrom-Json
20+
gh release download "$($release.tag_name)" --repo "${{ github.repository }}"
21+
2422
2523

mdresgen/IconDiff.cs

Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
using System.Collections;
2+
using System.IO.Compression;
3+
using System.Reflection;
4+
using System.Runtime.Loader;
5+
using System.Text.RegularExpressions;
6+
7+
namespace mdresgen;
8+
9+
internal static partial class IconDiff
10+
{
11+
[GeneratedRegex(@"MaterialDesignThemes\.(?<Version>\d+\.\d+\.\d+)\.nupkg")]
12+
private static partial Regex FileNameRegex();
13+
14+
public static async Task RunAsync()
15+
{
16+
var nugetDir = Path.Combine(PathHelper.RepositoryRoot, "nugets");
17+
var nugets =(
18+
from file in Directory.EnumerateFiles(nugetDir)
19+
let match = FileNameRegex().Match(Path.GetFileName(file))
20+
where match.Success
21+
let version = Version.Parse(match.Groups["Version"].Value)
22+
orderby version
23+
select (File: new FileInfo(file), Version: version)).ToList();
24+
25+
var oldNuget = nugets.First();
26+
var newNuget = nugets.Last();
27+
28+
string output = await CompareNuGets(oldNuget.File, newNuget.File);
29+
30+
await File.WriteAllTextAsync(Path.Combine(PathHelper.RepositoryRoot, $"IconChanges-{GetVersionString(oldNuget.Version)}--{GetVersionString(newNuget.Version)}.md"), output);
31+
32+
static string GetVersionString(Version version)
33+
=> $"{version.Major}.{version.Minor}.{version.Build}";
34+
}
35+
36+
private static async Task<string> CompareNuGets(FileInfo previousNuget, FileInfo currentNuget)
37+
{
38+
Console.WriteLine($"Comparing previous {previousNuget.Name} to {currentNuget.Name}");
39+
var previousValues = await ProcessNuGet(previousNuget) ?? throw new InvalidOperationException($"Failed to find icons in previous NuGet {previousNuget.FullName}");
40+
var newValues = await ProcessNuGet(currentNuget) ?? throw new InvalidOperationException($"Failed to find icons in current NuGet {currentNuget.FullName}");
41+
42+
var previousValuesByName = new Dictionary<string, int>();
43+
foreach (var kvp in previousValues)
44+
{
45+
foreach (string aliases in kvp.Value.Aliases)
46+
{
47+
previousValuesByName[aliases] = kvp.Key;
48+
}
49+
}
50+
var newValuesByName = new Dictionary<string, int>();
51+
foreach (var kvp in newValues)
52+
{
53+
foreach (string aliases in kvp.Value.Aliases)
54+
{
55+
newValuesByName[aliases] = kvp.Key;
56+
}
57+
}
58+
59+
var newItems = newValuesByName.Keys.Except(previousValuesByName.Keys)
60+
.OrderBy(x => x)
61+
.ToList();
62+
63+
var removedItems = previousValuesByName.Keys.Except(newValuesByName.Keys)
64+
.OrderBy(x => x)
65+
.ToList();
66+
67+
var visuallyChanged = newValuesByName.Keys.Intersect(previousValuesByName.Keys)
68+
.Where(key => newValues[newValuesByName[key]].Path != previousValues[previousValuesByName[key]].Path)
69+
.OrderBy(x => x)
70+
.ToList();
71+
72+
StringBuilder output = new();
73+
output.AppendLine("## Pack Icon Changes");
74+
WriteIconChanges("New icons", newItems, newValuesByName);
75+
76+
WriteIconChanges("Icons with visual changes", visuallyChanged, newValuesByName);
77+
78+
WriteIconChanges("Removed icons", removedItems, previousValuesByName);
79+
80+
return output.ToString();
81+
82+
void WriteIconChanges(string header, List<string> icons, Dictionary<string, int> iconsByName)
83+
{
84+
Console.WriteLine($"{header} => {icons.Count}");
85+
output.AppendLine($"### {header} ({icons.Count})");
86+
if (icons.Any())
87+
{
88+
foreach (var iconGroup in icons.GroupBy(name => iconsByName[name]))
89+
{
90+
output.AppendLine($"- {string.Join(", ", iconGroup)}");
91+
}
92+
}
93+
else
94+
{
95+
output.AppendLine("_None_");
96+
}
97+
}
98+
}
99+
100+
private static async Task<IReadOnlyDictionary<int, (HashSet<string> Aliases, string? Path)>?> ProcessNuGet(FileInfo nuget)
101+
{
102+
ZipArchive zipArchive = ZipFile.OpenRead(nuget.FullName);
103+
var entry = zipArchive.Entries.Where(x => x.FullName.EndsWith("MaterialDesignThemes.Wpf.dll"))
104+
.OrderByDescending(x => GetMajorTfmVersion(x.FullName))
105+
.First();
106+
107+
using MemoryStream ms = new();
108+
using (var entryStream = entry.Open())
109+
{
110+
await entryStream.CopyToAsync(ms);
111+
}
112+
ms.Position = 0;
113+
return ProcessDll(ms);
114+
115+
//This technically puts netcore before net framework, but since we are already at net7 and only care about latest
116+
//that does not matter.
117+
static int? GetMajorTfmVersion(string entryName)
118+
{
119+
if (!entryName.StartsWith("lib/net")) return null;
120+
string tfm = entryName[7..];
121+
char c = tfm.FirstOrDefault(char.IsDigit);
122+
if (c != default)
123+
{
124+
return int.Parse($"{c}");
125+
}
126+
return null;
127+
}
128+
}
129+
130+
private static IReadOnlyDictionary<int, (HashSet<string> Aliases, string? Path)>? ProcessDll(Stream assemblyStream)
131+
{
132+
AssemblyLoadContext context = new AssemblyLoadContext(Guid.NewGuid().ToString(), true);
133+
134+
Assembly assembly = context.LoadFromStream(assemblyStream);
135+
136+
Type? packIconKind = assembly.GetType("MaterialDesignThemes.Wpf.PackIconKind");
137+
Type? packIconDataFactory = assembly.GetType("MaterialDesignThemes.Wpf.PackIconDataFactory");
138+
139+
if (packIconKind is null) return null;
140+
if (packIconDataFactory is null) return null;
141+
142+
var rv = new Dictionary<int, (HashSet<string>, string?)>();
143+
144+
MethodInfo? createMethod = packIconDataFactory.GetMethod("Create", BindingFlags.InvokeMethod | BindingFlags.NonPublic | BindingFlags.Static);
145+
146+
var pathDictionary = (IDictionary?)createMethod?.Invoke(null, Array.Empty<object?>());
147+
148+
if (pathDictionary is null) return null;
149+
150+
foreach (string enumName in Enum.GetNames(packIconKind))
151+
{
152+
object @enum = Enum.Parse(packIconKind, enumName);
153+
if (rv.TryGetValue((int)@enum, out var found))
154+
{
155+
found.Item1.Add(enumName);
156+
continue;
157+
}
158+
159+
string? path = (string?)pathDictionary[@enum];
160+
rv[(int)@enum] = (new HashSet<string> { enumName }, path);
161+
}
162+
163+
context.Unload();
164+
165+
return rv;
166+
}
167+
}

0 commit comments

Comments
 (0)