Skip to content

Commit 07cf701

Browse files
committed
Add metapackage, add Release job to workflow, more changes
1 parent 771b619 commit 07cf701

File tree

7 files changed

+282
-13696
lines changed

7 files changed

+282
-13696
lines changed

.github/workflows/dotnet.yml

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,13 @@ on:
33
push:
44
pull_request:
55
merge_group:
6+
workflow_dispatch:
7+
inputs:
8+
release:
9+
description: "Release to NuGet"
10+
required: false
11+
type: boolean
12+
default: false
613
permissions:
714
statuses: write
815
checks: write
@@ -27,7 +34,7 @@ jobs:
2734
Pack
2835
--configuration Release
2936
--msbuild-properties
30-
VersionSuffix=${{ github.event_name != 'pull_request' && format('build{0}.0', github.run_number) || format('pr{0}.{1}', github.event.number, github.run_number) }}
37+
${{ github.event_name == 'workflow_dispatch' && inputs.release && '' || github.event_name != 'pull_request' && format('VersionSuffix=build{0}.0', github.run_number) || format('VersionSuffix=pr{0}.{1}', github.event.number, github.run_number) }}
3138
ContinuousIntegrationBuild=true
3239
- name: Upload Unsigned Artifacts to Actions
3340
uses: actions/upload-artifact@v4
@@ -81,6 +88,7 @@ jobs:
8188
name: coverage-${{ matrix.name }}
8289
path: ./coverage/**/coverage.cobertura.xml
8390
Report-Coverage:
91+
name: "Report Coverage"
8492
runs-on: ubuntu-latest
8593
needs: Test
8694
permissions:
@@ -107,3 +115,26 @@ jobs:
107115
with:
108116
path: coveragereport/Cobertura.xml
109117
minimum_coverage: 0
118+
Release:
119+
runs-on: windows-latest
120+
if: ${{ github.event_name == 'workflow_dispatch' && inputs.release }}
121+
needs: [Build, Test]
122+
environment: Release
123+
steps:
124+
- uses: actions/checkout@v3
125+
- uses: actions/download-artifact@v3
126+
with:
127+
path: "artifacts/**/*nupkg"
128+
- name: Push to NuGet
129+
if: ${{ github.repository == 'dotnet/Silk.NET' }}
130+
run: >-
131+
./build.sh SignPackages PushToNuGet FinishRelease --skip Clean Compile Pack
132+
--nuget-api-key ${{ secrets.GITHUB_TOKEN }}
133+
--akv-certificate ${{ secrets.AKV_CERTIFICATE }}
134+
--akv-client-id ${{ secrets.AKV_CLIENT_ID }}
135+
--akv-client-secret ${{ secrets.AKV_CLIENT_SECRET }}
136+
--akv-tenant ${{ secrets.AKV_TENANT }}
137+
--akv-vault-url ${{ secrets.AKV_VAULT_URL }}
138+
--discord-webhook ${{ secrets.DISCORD_ANNOUNCEMENT_WEBHOOK }}
139+
env:
140+
SILK_ACTIONS_DEPLOY_KEY: ${{ secrets.SILK_ACTIONS_DEPLOY_KEY }}

Silk.NET.sln

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SilkTouch", "SilkTouch", "{
9191
EndProject
9292
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Silk.NET.NUKE", "eng\build\Silk.NET.NUKE.csproj", "{3CADD95A-179F-4ECF-A49D-4B753832C63C}"
9393
EndProject
94+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Silk.NET", "sources\Core\Silk.NET\Silk.NET.csproj", "{6FA628B8-9696-4847-89F9-E58F470AF4FB}"
95+
EndProject
96+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Win32", "Win32", "{6E739132-EEAB-43A5-83C7-EB58C50D03A1}"
97+
EndProject
9498
Global
9599
GlobalSection(SolutionConfigurationPlatforms) = preSolution
96100
Debug|Any CPU = Debug|Any CPU
@@ -145,6 +149,10 @@ Global
145149
{3CADD95A-179F-4ECF-A49D-4B753832C63C}.Debug|Any CPU.Build.0 = Debug|Any CPU
146150
{3CADD95A-179F-4ECF-A49D-4B753832C63C}.Release|Any CPU.ActiveCfg = Release|Any CPU
147151
{3CADD95A-179F-4ECF-A49D-4B753832C63C}.Release|Any CPU.Build.0 = Release|Any CPU
152+
{6FA628B8-9696-4847-89F9-E58F470AF4FB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
153+
{6FA628B8-9696-4847-89F9-E58F470AF4FB}.Debug|Any CPU.Build.0 = Debug|Any CPU
154+
{6FA628B8-9696-4847-89F9-E58F470AF4FB}.Release|Any CPU.ActiveCfg = Release|Any CPU
155+
{6FA628B8-9696-4847-89F9-E58F470AF4FB}.Release|Any CPU.Build.0 = Release|Any CPU
148156
EndGlobalSection
149157
GlobalSection(SolutionProperties) = preSolution
150158
HideSolutionNode = FALSE
@@ -172,6 +180,8 @@ Global
172180
{D2B9C43F-A80D-4C9A-9643-BC1AC1B4E807} = {49D426BF-A009-43D5-A9E2-EFAAAA7196FC}
173181
{600D712C-4ABF-44C4-96C3-B1DEE1F38298} = {AB25C482-DA9D-4335-8E26-2F29C3700152}
174182
{3CADD95A-179F-4ECF-A49D-4B753832C63C} = {475AEF7B-0154-4989-AF82-97E3A95A96AF}
183+
{6FA628B8-9696-4847-89F9-E58F470AF4FB} = {5CD096DB-6C44-48F1-9093-AD4C84B6B7EC}
184+
{6E739132-EEAB-43A5-83C7-EB58C50D03A1} = {DD29EA8F-B1A6-45AA-8D2E-B38DA56D9EF6}
175185
EndGlobalSection
176186
GlobalSection(ExtensibilityGlobals) = postSolution
177187
SolutionGuid = {78D2CF6A-60A1-43E3-837B-00B73C9DA384}

eng/build/Build.Publishing.cs

Lines changed: 186 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,22 @@
33

44
using System;
55
using System.Collections.Generic;
6+
using System.IO;
67
using System.Linq;
8+
using System.Net.Http;
9+
using System.Net.Http.Json;
10+
using System.Text;
11+
using System.Text.Json.Serialization;
712
using System.Threading.Tasks;
813
using Nuke.Common;
14+
using Nuke.Common.CI.GitHubActions;
915
using Nuke.Common.IO;
1016
using Nuke.Common.Tooling;
1117
using Nuke.Common.Tools.DotNet;
18+
using Octokit;
1219
using Serilog;
1320
using static Nuke.Common.Tools.DotNet.DotNetTasks;
21+
using static Nuke.Common.Tools.Git.GitTasks;
1422

1523
partial class Build
1624
{
@@ -126,4 +134,182 @@ DotNetNuGetPushSettings NuGetPushSettings
126134
return ret;
127135
}
128136
}
137+
138+
private void GetVersionInfo(
139+
string changelog,
140+
out string silkVersion,
141+
out string silkVersionSuffix,
142+
out string silkReleaseNotes
143+
)
144+
{
145+
var lines = changelog.Split(["\r\n", "\r", "\n"], StringSplitOptions.None);
146+
147+
var capturing = false;
148+
var notes = new StringBuilder();
149+
string? version = null;
150+
string? versionSuffix = null;
151+
foreach (var theLine in lines)
152+
{
153+
var line = theLine.Trim();
154+
if (!line.StartsWith("##"))
155+
{
156+
if (capturing)
157+
{
158+
notes.AppendLine(line);
159+
}
160+
continue;
161+
}
162+
163+
if (capturing)
164+
{
165+
break;
166+
}
167+
168+
version = line[2..].Trim();
169+
var versionStop = version.IndexOf(' ');
170+
if (versionStop != -1)
171+
{
172+
version = version[..versionStop];
173+
}
174+
versionSuffix = string.Empty;
175+
var versionSuffixStart = version.IndexOf('-');
176+
if (versionSuffixStart != -1)
177+
{
178+
versionSuffix = version[(versionSuffixStart + 1)..];
179+
version = version[..versionSuffixStart];
180+
}
181+
versionSuffix = versionSuffix.Trim();
182+
version = version.Trim();
183+
capturing = true;
184+
}
185+
186+
if (!capturing)
187+
{
188+
throw new Exception("Failed to determine version information.");
189+
}
190+
191+
silkVersion = version ?? "3.0.0";
192+
silkVersionSuffix = versionSuffix ?? "unknown";
193+
silkReleaseNotes = notes.ToString();
194+
}
195+
196+
private async Task CreateReleaseAsync(string version, string versionSuffix, string releaseNotes)
197+
{
198+
var tag =
199+
$"v{version}{(string.IsNullOrWhiteSpace(versionSuffix) ? string.Empty : $"-{versionSuffix}")}";
200+
if (Environment.GetEnvironmentVariable("SILK_ACTIONS_DEPLOY_KEY") is { } deployKey)
201+
{
202+
await File.WriteAllTextAsync(TemporaryDirectory / "deploy_key.pem", deployKey);
203+
Git(
204+
$"config core.sshCommand \"ssh -i \\\"{TemporaryDirectory / "deploy_key.pem"}\\\"\""
205+
);
206+
}
207+
208+
Git($"config user.email \"[email protected]\"");
209+
Git($"config user.name \"The Silk.NET Automaton\"");
210+
Git($"tag {tag}");
211+
Git($"push origin {tag}");
212+
var github = new GitHubClient(
213+
new ProductHeaderValue("Silk.NET-CI"),
214+
new Octokit.Internal.InMemoryCredentialStore(
215+
new Credentials(GitHubActions.Instance.Token)
216+
)
217+
);
218+
var lines = releaseNotes
219+
.Split(["\r\n", "\r", "\n"], StringSplitOptions.None)
220+
.SkipWhile(string.IsNullOrWhiteSpace)
221+
.ToList();
222+
await github.Repository.Release.Create(
223+
"dotnet",
224+
"Silk.NET",
225+
new NewRelease(tag)
226+
{
227+
Prerelease = !string.IsNullOrWhiteSpace(versionSuffix),
228+
Body = releaseNotes,
229+
DiscussionCategoryName = "Q&A",
230+
Name = lines[0].Replace("Silk.NET", string.Empty).Trim(), // tradition
231+
TargetCommitish = GitCurrentCommit(),
232+
}
233+
);
234+
}
235+
236+
private void CommitShippedApi()
237+
{
238+
if (GitIsDetached())
239+
{
240+
Git($"checkout {GitHubActions.Instance.RefName}");
241+
}
242+
Git("add **/PublicAPI.*.txt");
243+
Git("commit -m \"Update public API after release\"");
244+
}
245+
246+
private readonly record struct VersionsList(
247+
[property: JsonPropertyName("versions")] List<string> Versions
248+
);
249+
250+
private async Task WaitForNuGetToUpdateAsync(string version, string versionSuffix)
251+
{
252+
var fullVersion = string.IsNullOrWhiteSpace(versionSuffix)
253+
? version
254+
: $"{version}-{versionSuffix}";
255+
using var http = new HttpClient();
256+
for (var i = 0; i < 60; i++)
257+
{
258+
var versions = await (
259+
await http.GetAsync("https://api.nuget.org/v3-flatcontainer/silk.net/index.json")
260+
)
261+
.EnsureSuccessStatusCode()
262+
.Content.ReadFromJsonAsync<VersionsList>();
263+
if (versions.Versions.Contains(fullVersion))
264+
{
265+
return;
266+
}
267+
268+
await Task.Delay(TimeSpan.FromMinutes(1));
269+
}
270+
271+
throw new Exception("NuGet didn't update after an hour.");
272+
}
273+
274+
[Parameter("Discord release announcement webhook.")]
275+
readonly string? DiscordWebhook;
276+
277+
private readonly record struct Webhook(
278+
[property: JsonPropertyName("content")] string Content,
279+
[property: JsonPropertyName("allowed_mentions")]
280+
Dictionary<string, string[]> AllowedMentions
281+
);
282+
283+
private async Task SendWebhookAsync(string version, string versionSuffix, string releaseNotes)
284+
{
285+
if (DiscordWebhook is null)
286+
{
287+
return;
288+
}
289+
290+
var fullVersion = string.IsNullOrWhiteSpace(versionSuffix)
291+
? version
292+
: $"{version}-{versionSuffix}";
293+
var lines = releaseNotes
294+
.Trim()
295+
.Split(["\r\n", "\r", "\n"], StringSplitOptions.None)
296+
.SkipWhile(string.IsNullOrWhiteSpace)
297+
.ToList();
298+
var headline = $"{lines[0].Trim()} (v{fullVersion})";
299+
var message =
300+
$"**__{headline}__** <@335783158223994890>\n\n"
301+
+ $"Get it on NuGet: https://nuget.org/packages/Silk.NET/{fullVersion}\n\n"
302+
+ string.Join('\n', lines.Skip(1).SkipWhile(string.IsNullOrWhiteSpace))
303+
+ $"\n\n**__Get {headline} on NuGet__**";
304+
using var http = new HttpClient();
305+
(
306+
await http.PostAsJsonAsync(
307+
DiscordWebhook,
308+
new Webhook(
309+
message,
310+
new Dictionary<string, string[]> { { "users", ["335783158223994890"] } }
311+
)
312+
)
313+
).EnsureSuccessStatusCode();
314+
}
129315
}

eng/build/Build.cs

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
using System.IO;
12
using Nuke.Common;
23
using Nuke.Common.IO;
34
using Nuke.Common.Tools.DotNet;
@@ -67,14 +68,15 @@ partial class Build : NukeBuild
6768
)
6869
);
6970

70-
Target ShipApi => CommonTarget(x => x.Executes(MoveUnshippedContentsToShipped));
71+
Target ShipApi =>
72+
CommonTarget(x => x.After(PushToNuGet).Executes(MoveUnshippedContentsToShipped));
7173

7274
Target SignPackages =>
7375
CommonTarget(x => x.Before(PushToNuGet).After(Pack).Executes(CodesignAllPackageOutputs));
7476

7577
Target PushToNuGet =>
7678
CommonTarget(x =>
77-
x.After(Pack)
79+
x.After(Pack, SignPackages)
7880
.Executes(async () =>
7981
{
8082
AddTemporaryFeed();
@@ -91,4 +93,23 @@ partial class Build : NukeBuild
9193
}
9294
})
9395
);
96+
97+
Target FinishRelease =>
98+
CommonTarget(x =>
99+
x.After(PushToNuGet)
100+
.DependsOn(ShipApi)
101+
.Executes(async () =>
102+
{
103+
GetVersionInfo(
104+
File.ReadAllText(RootDirectory / "docs" / "CHANGELOG.md"),
105+
out var version,
106+
out var versionSuffix,
107+
out var releaseNotes
108+
);
109+
await CreateReleaseAsync(version, versionSuffix, releaseNotes);
110+
CommitShippedApi();
111+
await WaitForNuGetToUpdateAsync(version, versionSuffix);
112+
await SendWebhookAsync(version, versionSuffix, releaseNotes);
113+
})
114+
);
94115
}

eng/build/Silk.NET.NUKE.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,5 +13,6 @@
1313

1414
<ItemGroup>
1515
<PackageReference Include="Nuke.Common" Version="9.0.3" />
16+
<PackageReference Include="Octokit" Version="13.0.1" />
1617
</ItemGroup>
1718
</Project>
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<TargetFramework>net8.0</TargetFramework>
5+
<ImplicitUsings>enable</ImplicitUsings>
6+
<Nullable>enable</Nullable>
7+
<SilkDescription>A package that will pull in all non-extension Silk.NET packages for every API.</SilkDescription>
8+
<SilkExtendedDescription>You probably don't want all that, and it is strongly recommended to pull in only the packages you need instead. This package exists just to simplify project bring-up.</SilkExtendedDescription>
9+
<IncludeBuildOutput>false</IncludeBuildOutput>
10+
<SilkSourceLinkExempt>true</SilkSourceLinkExempt>
11+
</PropertyGroup>
12+
13+
<ItemGroup>
14+
<!-- Include sources/A/A/Silk.NET.A.csproj in the metapackage -->
15+
<SilkSourceRoot Include="$([System.IO.Directory]::GetDirectories(&quot;$(MSBuildThisFileDirectory)../../&quot;))" />
16+
<SilkSourceCategory Include="@(SilkSourceRoot->'%(Filename)')" />
17+
<ProjectReference Include="@(SilkSourceCategory->'../../%(Filename)/%(Filename)/Silk.NET.%(Filename).csproj'->Exists())" />
18+
<ProjectReference Remove="../../Win32/Win32/Silk.NET.Win32.csproj" /> <!-- TODO remove once Win32 is worked on -->
19+
<ProjectReference Remove="../../SilkTouch/SilkTouch/Silk.NET.SilkTouch.csproj" /> <!-- Not user facing today. -->
20+
<SilkSourceCategory Remove="@(SilkSourceCategory)" />
21+
<SilkSourceRoot Remove="@(SilkSourceRoot)" />
22+
</ItemGroup>
23+
24+
</Project>

0 commit comments

Comments
 (0)