1
- // Copyright (c) Duende Software. All rights reserved.
1
+ // Copyright (c) Duende Software. All rights reserved.
2
2
// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.
3
3
4
4
using Logicality . GitHub . Actions . Workflow ;
33
33
GenerateReleaseWorkflow ( component ) ;
34
34
}
35
35
36
+ GenerateUploadTestResultsWorkflow ( ) ;
37
+
38
+
36
39
void GenerateCiWorkflow ( Component component )
37
40
{
38
- var workflow = new Workflow ( $ " { component . Name } /ci" ) ;
41
+ var workflow = new Workflow ( component . CiWorkflowName ) ;
39
42
var paths = new [ ]
40
43
{
41
44
$ ".github/workflows/{ component . Name } -**",
@@ -49,7 +52,7 @@ void GenerateCiWorkflow(Component component)
49
52
. Push ( )
50
53
. Paths ( paths ) ;
51
54
workflow . On
52
- . PullRequestTarget ( )
55
+ . PullRequest ( )
53
56
. Paths ( paths ) ;
54
57
55
58
workflow . EnvDefaults ( ) ;
@@ -76,10 +79,10 @@ void GenerateCiWorkflow(Component component)
76
79
77
80
foreach ( var testProject in component . Tests )
78
81
{
79
- job . StepTestAndReport ( component . Name , testProject ) ;
82
+ job . StepTest ( component . Name , testProject ) ;
80
83
}
81
84
82
- job . StepInstallCACerts ( ) ;
85
+ job . StepUploadTestResultsAsArtifact ( component ) ;
83
86
84
87
job . StepToolRestore ( ) ;
85
88
@@ -90,8 +93,6 @@ void GenerateCiWorkflow(Component component)
90
93
91
94
job . StepSign ( ) ;
92
95
93
- job . StepPush ( "MyGet" , "https://www.myget.org/F/duende_identityserver/api/v2/package" , "MYGET" ) ;
94
-
95
96
job . StepPush ( "GitHub" , "https://nuget.pkg.github.com/DuendeSoftware/index.json" , "GITHUB_TOKEN" )
96
97
. Env ( ( "GITHUB_TOKEN" , contexts . Secrets . GitHubToken ) ,
97
98
( "NUGET_AUTH_TOKEN" , contexts . Secrets . GitHubToken ) ) ;
@@ -104,7 +105,7 @@ void GenerateCiWorkflow(Component component)
104
105
105
106
void GenerateReleaseWorkflow ( Component component )
106
107
{
107
- var workflow = new Workflow ( $ " { component . Name } /release" ) ;
108
+ var workflow = new Workflow ( component . ReleaseWorkflowName ) ;
108
109
109
110
workflow . On
110
111
. WorkflowDispatch ( )
@@ -138,16 +139,14 @@ git config --global user.name ""Duende Software GitHub Bot""
138
139
git tag -a { component . TagPrefix } -{ contexts . Event . Input . Version } -m ""Release v{ contexts . Event . Input . Version } ""
139
140
git push origin { component . TagPrefix } -{ contexts . Event . Input . Version } " ) ;
140
141
141
- tagJob . StepInstallCACerts ( ) ;
142
-
143
142
foreach ( var project in component . Projects )
144
143
{
145
144
tagJob . StepPack ( project ) ;
146
145
}
147
146
148
147
tagJob . StepToolRestore ( ) ;
149
148
150
- tagJob . StepSign ( ) ;
149
+ tagJob . StepSign ( true ) ;
151
150
152
151
tagJob . StepPush ( "MyGet" , "https://www.myget.org/F/duende_identityserver/api/v2/package" , "MYGET" ) ;
153
152
@@ -160,8 +159,7 @@ git tag -a {component.TagPrefix}-{contexts.Event.Input.Version} -m ""Release v{c
160
159
var publishJob = workflow . Job ( "publish" )
161
160
. Name ( "Publish to nuget.org" )
162
161
. RunsOn ( GitHubHostedRunners . UbuntuLatest )
163
- . Needs ( "tag" )
164
- . Environment ( "nuget.org" , "" ) ;
162
+ . Needs ( "tag" ) ;
165
163
166
164
publishJob . Step ( )
167
165
. Uses ( "actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16" ) // 4.1.8
@@ -180,20 +178,55 @@ git tag -a {component.TagPrefix}-{contexts.Event.Input.Version} -m ""Release v{c
180
178
WriteWorkflow ( workflow , fileName ) ;
181
179
}
182
180
181
+ void GenerateUploadTestResultsWorkflow ( )
182
+ {
183
+ var workflow = new Workflow ( "generate-test-reports" ) ;
184
+ workflow . On
185
+ . WorkflowRun ( )
186
+ . Workflows ( components . Select ( x => x . CiWorkflowName ) . ToArray ( ) )
187
+ . Types ( "completed" ) ;
188
+
189
+ var job = workflow
190
+ . Job ( "report" )
191
+ . Name ( "report" )
192
+ . RunsOn ( GitHubHostedRunners . UbuntuLatest ) ;
193
+
194
+ job . Permissions (
195
+ actions : Permission . Read ,
196
+ contents : Permission . Read ,
197
+ checks : Permission . Write ,
198
+ packages : Permission . Write ) ;
199
+
200
+ foreach ( var component in components )
201
+ {
202
+ foreach ( var testProject in component . Tests )
203
+ {
204
+ job . StepGenerateReportFromTestArtifact ( component , testProject ) ;
205
+ }
206
+ }
207
+
208
+ var fileName = $ "generate-test-reports";
209
+ WriteWorkflow ( workflow , fileName ) ;
210
+ }
211
+
183
212
void WriteWorkflow ( Workflow workflow , string fileName )
184
213
{
185
214
var filePath = $ "../workflows/{ fileName } .yml";
186
215
workflow . WriteYaml ( filePath ) ;
187
216
Console . WriteLine ( $ "Wrote workflow to { filePath } ") ;
188
217
}
189
218
190
- record Component ( string Name , string [ ] Projects , string [ ] Tests , string TagPrefix ) ;
219
+ record Component ( string Name , string [ ] Projects , string [ ] Tests , string TagPrefix )
220
+ {
221
+ public string CiWorkflowName => $ "{ Name } /ci";
222
+ public string ReleaseWorkflowName => $ "{ Name } /release";
223
+ }
191
224
192
225
public static class StepExtensions
193
226
{
194
227
public static void EnvDefaults ( this Workflow workflow )
195
228
=> workflow . Env (
196
- ( "DOTNETT_NOLOGO " , "true" ) ,
229
+ ( "DOTNET_NOLOGO " , "true" ) ,
197
230
( "DOTNET_CLI_TELEMETRY_OPTOUT" , "true" ) ) ;
198
231
199
232
public static void StepSetupDotNet ( this Job job )
@@ -204,41 +237,48 @@ public static void StepSetupDotNet(this Job job)
204
237
public static Step IfRefMain ( this Step step )
205
238
=> step . If ( "github.ref == 'refs/heads/main'" ) ;
206
239
207
- public static void StepTestAndReport ( this Job job , string componentName , string testProject )
240
+ public static void StepTest ( this Job job , string componentName , string testProject )
208
241
{
209
242
var path = $ "test/{ testProject } ";
210
- var logFileName = "Tests .trx";
243
+ var logFileName = $ " { testProject } .trx";
211
244
var flags = $ "--logger \" console;verbosity=normal\" " +
212
245
$ "--logger \" trx;LogFileName={ logFileName } \" " +
213
246
$ "--collect:\" XPlat Code Coverage\" ";
214
247
job . Step ( )
215
248
. Name ( $ "Test - { testProject } ")
216
249
. Run ( $ "dotnet test -c Release { path } { flags } ") ;
217
250
251
+ }
252
+
253
+ internal static void StepUploadTestResultsAsArtifact ( this Job job , Component component )
254
+ {
218
255
job . Step ( )
219
- . Name ( $ "Test report - { testProject } ")
220
- . Uses ( "dorny/test-reporter@31a54ee7ebcacc03a09ea97a7e5465a47b84aea5" ) // v1.9.1
256
+ . Name ( $ "Test report")
221
257
. If ( "success() || failure()" )
258
+ . Uses ( "actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882" ) // 4.4.3
259
+ . With (
260
+ ( "name" , "test-results" ) ,
261
+ ( "path" , string . Join ( Environment . NewLine , component . Tests
262
+ . Select ( testProject => $ "{ component . Name } /test/{ testProject } /TestResults/{ testProject } .trx") ) ) ,
263
+ ( "retention-days" , "5" ) ) ;
264
+ }
265
+
266
+ internal static void StepGenerateReportFromTestArtifact ( this Job job , Component component , string testProject )
267
+ {
268
+ var path = $ "test/{ testProject } ";
269
+ job . Step ( )
270
+ . Name ( $ "Test report - { component . Name } - { testProject } ")
271
+ . Uses ( "dorny/test-reporter@31a54ee7ebcacc03a09ea97a7e5465a47b84aea5" ) // v1.9.1
272
+ . If ( $ "github.event.workflow.name == '{ component . CiWorkflowName } '")
222
273
. With (
274
+ ( "artifact" , "test-results" ) ,
223
275
( "name" , $ "Test Report - { testProject } ") ,
224
- ( "path" , $ "{ componentName } / { path } /TestResults/ { logFileName } ") ,
276
+ ( "path" , $ "{ testProject } .trx ") ,
225
277
( "reporter" , "dotnet-trx" ) ,
226
278
( "fail-on-error" , "true" ) ,
227
279
( "fail-on-empty" , "true" ) ) ;
228
280
}
229
281
230
- // These intermediate certificates are required for signing and are not installed on the GitHub runners by default.
231
- public static void StepInstallCACerts ( this Job job )
232
- => job . Step ( )
233
- . Name ( "Install Sectigo CodeSiging CA certificates" )
234
- . WorkingDirectory ( ".github/workflows" )
235
- . Run ( """
236
- sudo apt-get update
237
- sudo apt-get install -y ca-certificates
238
- sudo cp SectigoPublicCodeSigningRootCrossAAA.crt /usr/local/share/ca-certificates/
239
- sudo update-ca-certificates
240
- """ ) ;
241
-
242
282
public static void StepToolRestore ( this Job job )
243
283
=> job . Step ( )
244
284
. Name ( "Tool restore" )
@@ -252,23 +292,33 @@ public static void StepPack(this Job job, string project)
252
292
. Run ( $ "dotnet pack -c Release { path } -o artifacts") ;
253
293
}
254
294
255
- public static void StepSign ( this Job job )
295
+ public static void StepSign ( this Job job , bool always = false )
256
296
{
257
- var flags = "--file-digest sha256 " +
258
- "--timestamp-rfc3161 http://timestamp.digicert.com " +
259
- "--azure-key-vault-url https://duendecodesigning.vault.azure.net/ " +
260
- "--azure-key-vault-client-id 18e3de68-2556-4345-8076-a46fad79e474 " +
261
- "--azure-key-vault-tenant-id ed3089f0-5401-4758-90eb-066124e2d907 " +
262
- "--azure-key-vault-client-secret ${{ secrets.SignClientSecret }} " +
263
- "--azure-key-vault-certificate CodeSigning" ;
264
- job . Step ( )
265
- . Name ( "Sign packages" )
266
- . Run ( $ """
267
- for file in artifacts/*.nupkg; do
268
- dotnet NuGetKeyVaultSignTool sign "$file" { flags }
269
- done
270
- """ ) ;
297
+ var flags = "--file-digest sha256 " +
298
+ "--timestamp-rfc3161 http://timestamp.digicert.com " +
299
+ "--azure-key-vault-url https://duendecodesigninghsm.vault.azure.net/ " +
300
+ "--azure-key-vault-client-id 18e3de68-2556-4345-8076-a46fad79e474 " +
301
+ "--azure-key-vault-tenant-id ed3089f0-5401-4758-90eb-066124e2d907 " +
302
+ "--azure-key-vault-client-secret ${{ secrets.SignClientSecret }} " +
303
+ "--azure-key-vault-certificate NuGetPackageSigning" ;
304
+ var step = job . Step ( )
305
+ . Name ( "Sign packages" ) ;
306
+ if ( ! always )
307
+ {
308
+ step = step . IfGithubEventIsPush ( ) ;
309
+ }
310
+ step . Run ( $ """
311
+ for file in artifacts/*.nupkg; do
312
+ dotnet NuGetKeyVaultSignTool sign "$file" { flags }
313
+ done
314
+ """ ) ;
271
315
}
316
+ /// <summary>
317
+ /// Only run this if the build is triggered on a branch IN the same repo
318
+ /// this means it's from a trusted contributor.
319
+ /// </summary>
320
+ public static Step IfGithubEventIsPush ( this Step step )
321
+ => step . If ( "github.event_name == 'push'" ) ;
272
322
273
323
public static Step StepPush ( this Job job , string destination , string sourceUrl , string secretName )
274
324
{
@@ -284,7 +334,7 @@ public static void StepUploadArtifacts(this Job job, string componentName)
284
334
var path = $ "{ componentName } /artifacts/*.nupkg";
285
335
job . Step ( )
286
336
. Name ( "Upload Artifacts" )
287
- . IfRefMain ( )
337
+ . IfGithubEventIsPush ( )
288
338
. Uses ( "actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882" ) // 4.4.3
289
339
. With (
290
340
( "name" , "artifacts" ) ,
@@ -326,4 +376,4 @@ public class EventsInputContext() : Context("github.event.inputs")
326
376
{
327
377
public string Version => Expression ( $ "{ Name } .version") ;
328
378
}
329
- }
379
+ }
0 commit comments