Skip to content

Commit 07f34e7

Browse files
authored
Merge pull request #4394 from devlead/feature/gh-4391
GH4391: GHA cross-job download artifact support
2 parents cf55842 + f730597 commit 07f34e7

File tree

6 files changed

+153
-19
lines changed

6 files changed

+153
-19
lines changed

.github/workflows/build.yml

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,19 @@ on:
77
- develop
88
- hotfix/*
99
jobs:
10+
prepare:
11+
name: Prepare integration tests
12+
runs-on: ubuntu-latest
13+
steps:
14+
- run: echo "Cake Integration Tests" > cake-integration-tests.txt
15+
- uses: actions/upload-artifact@v4
16+
with:
17+
name: cake-integration-tests
18+
path: cake-integration-tests.txt
19+
1020
build:
1121
name: Build
22+
needs: prepare
1223
runs-on: ${{ matrix.os }}
1324
strategy:
1425
fail-fast: false
@@ -34,7 +45,7 @@ jobs:
3445

3546
- name: Run Cake script
3647
id: build-cake
37-
uses: cake-build/cake-action@v1
48+
uses: cake-build/cake-action@master
3849
with:
3950
target: Run-Integration-Tests
4051
cake-version: tool-manifest

src/Cake.Common.Tests/Fixtures/Build/GitHubActionsCommandsFixture.cs

Lines changed: 48 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
using System.Threading.Tasks;
88
using Cake.Common.Build.GitHubActions.Commands;
99
using Cake.Common.Build.GitHubActions.Commands.Artifact;
10-
using Cake.Common.Build.GitHubActions.Data;
1110
using Cake.Common.Tests.Fakes;
1211
using Cake.Core;
1312
using Cake.Core.IO;
@@ -22,21 +21,45 @@ internal sealed class GitHubActionsCommandsFixture : HttpMessageHandler
2221
private const string ArtifactUrl = GitHubActionsInfoFixture.ActionResultsUrl + "twirp/github.actions.results.api.v1.ArtifactService/";
2322
private const string CreateArtifactUrl = ArtifactUrl + "CreateArtifact";
2423
private const string FinalizeArtifactUrl = ArtifactUrl + "FinalizeArtifact";
25-
private const string GetSignedArtifactURLurl = ArtifactUrl + "GetSignedArtifactURL";
24+
private const string GetSignedArtifactURLUrl = ArtifactUrl + "GetSignedArtifactURL";
25+
private const string ListArtifactsUrl = ArtifactUrl + "ListArtifacts";
2626
private const string UploadFileUrl = "https://cake.build.net/actions-results/a9d82106-d5d5-4310-8f60-0bfac035cf02/workflow-job-run-1d849a45-2f30-5fbb-3226-b730a17a93af/artifacts/91e64594182918fa8012cdbf7d1a4f801fa0c35f485c3277268aad8e3f45377c.zip?sig=upload";
2727
private const string DownloadFileUrl = "https://cake.build.net/actions-results/a9d82106-d5d5-4310-8f60-0bfac035cf02/workflow-job-run-1d849a45-2f30-5fbb-3226-b730a17a93af/artifacts/91e64594182918fa8012cdbf7d1a4f801fa0c35f485c3277268aad8e3f45377c.zip?sig=download";
28-
private const string CreateArtifactResponse = @"{
29-
""ok"": true,
30-
""signed_upload_url"": """ + UploadFileUrl + @"""
31-
}";
32-
private const string FinalizeArtifactResponse = @"{
33-
""ok"": true,
34-
""artifact_id"": ""1991105334""
35-
}";
36-
private const string GetSignedArtifactURLResponse = @"{
37-
""name"": ""artifact"",
38-
""signed_url"": """ + DownloadFileUrl + @"""
39-
}";
28+
private const string CreateArtifactResponse =
29+
$$"""
30+
{
31+
"ok": true,
32+
"signed_upload_url": "{{UploadFileUrl}}"
33+
}
34+
""";
35+
private const string FinalizeArtifactResponse =
36+
"""
37+
{
38+
"ok": true,
39+
"artifact_id": "1991105334"
40+
}
41+
""";
42+
private const string GetSignedArtifactURLResponse =
43+
$$"""
44+
{
45+
"name": "artifact",
46+
"signed_url": "{{DownloadFileUrl}}"
47+
}
48+
""";
49+
private const string ListArtifactsResponse =
50+
$$"""
51+
{
52+
"artifacts": [
53+
{
54+
"workflow_run_backend_id": "b9e28153-ca20-4b86-91dd-09e8f644efdf",
55+
"workflow_job_run_backend_id": "1d849a45-2f30-5fbb-3226-b730a17a93af",
56+
"database_id": "1",
57+
"name": "artifact",
58+
"created_at": "2024-11-09T21:53:00.7110204+00:00"
59+
}
60+
]
61+
}
62+
""";
4063

4164
private GitHubActionsInfoFixture GitHubActionsInfoFixture { get; }
4265
private ICakeEnvironment Environment { get; }
@@ -122,7 +145,7 @@ protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage
122145
// Get Signed Artifact Url
123146
case
124147
{
125-
RequestUri: { AbsoluteUri: GetSignedArtifactURLurl },
148+
RequestUri: { AbsoluteUri: GetSignedArtifactURLUrl },
126149
Method: { Method: "POST" },
127150
}:
128151
{
@@ -189,6 +212,16 @@ protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage
189212
return Ok();
190213
}
191214

215+
// List Artifacts
216+
case
217+
{
218+
RequestUri: { AbsoluteUri: ListArtifactsUrl },
219+
Method: { Method: "POST" }
220+
}:
221+
{
222+
return Ok(new StringContent(ListArtifactsResponse));
223+
}
224+
192225
// Download File
193226
case
194227
{

src/Cake.Common/Build/GitHubActions/Commands/Artifact/GitHubActionsArtifactService.cs

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,16 +30,43 @@ internal record GitHubActionsArtifactService(
3030
private static readonly Uri CreateArtifactUrl = new Uri("CreateArtifact", UriKind.Relative);
3131
private static readonly Uri FinalizeArtifactUrl = new Uri("FinalizeArtifact", UriKind.Relative);
3232
private static readonly Uri GetSignedArtifactURLUrl = new Uri("GetSignedArtifactURL", UriKind.Relative);
33+
private static readonly Uri ListArtifactsUrl = new Uri("ListArtifacts", UriKind.Relative);
3334

3435
internal async Task DownloadArtifactFiles(
3536
string artifactName,
3637
DirectoryPath directoryPath)
38+
{
39+
var listArtifactsResponse = await ListArtifacts(
40+
artifactName);
41+
42+
if (listArtifactsResponse.Artifacts.FirstOrDefault(artifact => artifact.Name == artifactName)
43+
is { WorkflowRunBackendId.Length: > 0 } and { WorkflowJobRunBackendId.Length: > 0 } artifact)
44+
{
45+
var signedArtifactURLResponse = await GetSignedArtifactURL(artifact.WorkflowRunBackendId, artifact.WorkflowJobRunBackendId, artifactName);
46+
47+
await DownloadArtifact(signedArtifactURLResponse.SignedUrl, directoryPath);
48+
}
49+
else
50+
{
51+
throw new CakeException($"Artifact {artifactName} not found.");
52+
}
53+
}
54+
55+
private async Task<ListArtifactsResponse> ListArtifacts(
56+
string nameFilter = null,
57+
long? idFilter = null)
3758
{
3859
GetWorkflowBackendIds(out var workflowRunBackendId, out var workflowJobRunBackendId);
3960

40-
var (_, signedUrl) = await GetSignedArtifactURL(workflowRunBackendId, workflowJobRunBackendId, artifactName);
61+
var listArtifactsRequest = new ListArtifactsRequest(
62+
workflowRunBackendId,
63+
workflowJobRunBackendId,
64+
nameFilter,
65+
idFilter);
4166

42-
await DownloadArtifact(signedUrl, directoryPath);
67+
return await PostArtifactService<ListArtifactsRequest, ListArtifactsResponse>(
68+
ListArtifactsUrl,
69+
listArtifactsRequest);
4370
}
4471

4572
private async Task DownloadArtifact(string signedUrl, DirectoryPath directoryPath)
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
// See the LICENSE file in the project root for more information.
4+
5+
using System.Text.Json.Serialization;
6+
7+
namespace Cake.Common.Build.GitHubActions.Commands.Artifact
8+
{
9+
#pragma warning disable SA1313 // Parameter names should begin with lower-case letter
10+
internal record ListArtifactsRequest(
11+
[property: JsonPropertyName("workflow_run_backend_id")]
12+
string WorkflowRunBackendId,
13+
[property: JsonPropertyName("workflow_job_run_backend_id")]
14+
string WorkflowJobRunBackendId,
15+
[property: JsonPropertyName("name_filter")]
16+
string NameFilter = null,
17+
[property: JsonPropertyName("id_filter")]
18+
long? IdFilter = null);
19+
#pragma warning restore SA1313 // Parameter names should begin with lower-case letter
20+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
// See the LICENSE file in the project root for more information.
4+
5+
using System;
6+
using System.Text.Json.Serialization;
7+
8+
namespace Cake.Common.Build.GitHubActions.Commands.Artifact
9+
{
10+
#pragma warning disable SA1313 // Parameter names should begin with lower-case letter
11+
internal record ListArtifactsResponse(
12+
[property: JsonPropertyName("artifacts")]
13+
ListArtifactsResponse.MonolithArtifact[] Artifacts)
14+
{
15+
internal record MonolithArtifact(
16+
[property: JsonPropertyName("workflow_run_backend_id")]
17+
string WorkflowRunBackendId,
18+
[property: JsonPropertyName("workflow_job_run_backend_id")]
19+
string WorkflowJobRunBackendId,
20+
[property: JsonPropertyName("database_id")]
21+
string DatabaseId,
22+
[property: JsonPropertyName("name")]
23+
string Name,
24+
[property: JsonPropertyName("created_at")]
25+
DateTimeOffset CreatedAt);
26+
}
27+
}

tests/integration/Cake.Common/Build/GitHubActions/GitHubActionsProvider.cake

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,21 @@ Task("Cake.Common.Build.GitHubActionsProvider.Commands.DownloadArtifact")
137137
Assert.True(FileHashEquals(data.AssemblyPath, targetArtifactPath), $"{data.AssemblyPath.FullPath}=={targetArtifactPath.FullPath}");
138138
});
139139

140+
Task("Cake.Common.Build.GitHubActionsProvider.Commands.DownloadArtifact.PreviousJob")
141+
.Does(async () => {
142+
// Given
143+
var targetPath = Paths.Temp.Combine("./Cake.Common.Build.GitHubActionsProvider.Commands.DownloadArtifact.PreviousJob");
144+
EnsureDirectoryExists(targetPath);
145+
var targetArtifactPath = targetPath.CombineWithFilePath("cake-integration-tests.txt");
146+
147+
// When
148+
await GitHubActions.Commands.DownloadArtifact("cake-integration-tests", targetPath);
149+
150+
// Then
151+
Assert.True(System.IO.File.Exists(targetArtifactPath.FullPath), $"{targetArtifactPath.FullPath} Missing");
152+
Assert.Equal("Cake Integration Tests\n", System.IO.File.ReadAllText(targetArtifactPath.FullPath));
153+
});
154+
140155
Task("Cake.Common.Build.GitHubActionsProvider.Environment.Runner.Architecture")
141156
.Does(() => {
142157
// Given / When
@@ -193,7 +208,8 @@ if (GitHubActions.Environment.Runtime.IsRuntimeAvailable)
193208
gitHubActionsProviderTask
194209
.IsDependentOn("Cake.Common.Build.GitHubActionsProvider.Commands.UploadArtifact.File")
195210
.IsDependentOn("Cake.Common.Build.GitHubActionsProvider.Commands.UploadArtifact.Directory")
196-
.IsDependentOn("Cake.Common.Build.GitHubActionsProvider.Commands.DownloadArtifact");
211+
.IsDependentOn("Cake.Common.Build.GitHubActionsProvider.Commands.DownloadArtifact")
212+
.IsDependentOn("Cake.Common.Build.GitHubActionsProvider.Commands.DownloadArtifact.PreviousJob");
197213
}
198214

199215
public class GitHubActionsData

0 commit comments

Comments
 (0)