Skip to content

Commit e512c9a

Browse files
authored
feat(get-latest-workflow-artifact): consider in-progress runs (#1022)
In some situations a workflow might get triggered that depends on an artifact while the producing workflow is still in-progress. There might even be the case that the artifact hasn't been uploaded yet and so this scenario also allows for 5 retries taking place. The action now also returns the status of the used workflow as output.
1 parent f008500 commit e512c9a

File tree

2 files changed

+92
-43
lines changed

2 files changed

+92
-43
lines changed

actions/get-latest-workflow-artifact/README.md

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -8,17 +8,19 @@ Required permissions:
88
- `pull-requests: read`
99
- `actions: read`
1010

11-
| Input | Description | Required | Default |
12-
| --------------- | ---------------------------------- | -------- | ----------------------------------------- |
13-
| `workflow-id` | ID or filename of the workflow | yes | |
14-
| `artifact-name` | Name of the artifact to download | yes | |
15-
| `repository` | Owner/Name | no | `${{ github.repository }}` |
16-
| `pr-number` | Number of the PR to consider | no | `${{ github.event.pull_request.number }}` |
17-
| `path` | Directory to store the artifact in | no | `${{ github.workspace }}` |
18-
| `github-token` | GitHub token | no | `${{ github.token }}` |
11+
| Input | Description | Required | Default |
12+
| --------------------- | -------------------------------------------------------------- | -------- | ----------------------------------------- |
13+
| `workflow-id` | ID or filename of the workflow | yes | |
14+
| `artifact-name` | Name of the artifact to download | yes | |
15+
| `repository` | Owner/Name | no | `${{ github.repository }}` |
16+
| `pr-number` | Number of the PR to consider | no | `${{ github.event.pull_request.number }}` |
17+
| `path` | Directory to store the artifact in | no | `${{ github.workspace }}` |
18+
| `github-token` | GitHub token | no | `${{ github.token }}` |
19+
| `consider-inprogress` | Not only consider completed but also in-progress workflow runs | no | `false` |
1920

20-
| Output | Description |
21-
| ------------------------ | -------------------------------------- |
22-
| `artifact-download-path` | Path where the artifact was downloaded |
23-
| `artifact-id` | ID of the artifact that was downloaded |
24-
| `workflow-run-id` | ID of the Workflow Run |
21+
| Output | Description |
22+
| ------------------------ | ------------------------------------------------------ |
23+
| `artifact-download-path` | Path where the artifact was downloaded |
24+
| `artifact-id` | ID of the artifact that was downloaded |
25+
| `workflow-run-id` | ID of the Workflow Run |
26+
| `workflow-run-status` | Status of the found run (`in_progress` or `completed`) |

actions/get-latest-workflow-artifact/src/main.ts

Lines changed: 77 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ async function main() {
1111
.split("/");
1212
const prNumber = parseInt(core.getInput("pr-number", { required: true }), 10);
1313
const artifactName = core.getInput("artifact-name", { required: true });
14+
const considerInProgress = core.getInput("consider-inprogress") === "true";
1415
let path = core.getInput("path", { required: false });
1516

1617
if (!path) {
@@ -32,50 +33,96 @@ async function main() {
3233
});
3334

3435
const headSha = data.head.sha;
35-
// Now let's get all workflows associated with this sha.
36-
const { data: runs } = await client.rest.actions.listWorkflowRuns({
37-
repo: repoName,
38-
owner: repoOwner,
39-
head_sha: headSha,
40-
workflow_id: workflowId,
41-
status: "completed",
42-
});
4336

44-
if (runs.workflow_runs.length <= 0) {
45-
throw new Error(`No workflow runs found for sha ${headSha}`);
46-
}
37+
// Now let's get all workflows associated with this sha:
38+
// We start with in-progress ones if those are to be considered.
39+
let found = null;
4740

48-
const latestRun = runs.workflow_runs[0];
49-
core.info(`Latest run: ${latestRun.id} <${latestRun.html_url}>`);
41+
if (considerInProgress) {
42+
found = await getLatestArtifact(
43+
client,
44+
repoOwner,
45+
repoName,
46+
workflowId,
47+
headSha,
48+
"in_progress",
49+
artifactName,
50+
);
51+
core.setOutput("workflow-run-status", "in_progress");
52+
}
5053

51-
// Now that we have the run we can get a list of all artifacts there:
52-
const { data: artifacts } =
53-
await client.rest.actions.listWorkflowRunArtifacts({
54-
owner: repoOwner,
55-
repo: repoName,
56-
run_id: latestRun.id,
57-
});
54+
if (!found) {
55+
found = await getLatestArtifact(
56+
client,
57+
repoOwner,
58+
repoName,
59+
workflowId,
60+
headSha,
61+
"completed",
62+
artifactName,
63+
);
64+
core.setOutput("workflow-run-status", "completed");
65+
}
5866

59-
const artifact = new DefaultArtifactClient();
60-
const candidates = artifacts.artifacts.filter(
61-
(artifact) => artifact.name == artifactName,
62-
);
63-
if (candidates.length <= 0) {
67+
if (!found) {
6468
throw new Error(`No artifacts found with name ${artifactName}`);
6569
}
66-
67-
const response = await artifact.downloadArtifact(candidates[0].id, {
70+
const { artifact: foundArtifact, run } = found;
71+
const artifact = new DefaultArtifactClient();
72+
const response = await artifact.downloadArtifact(foundArtifact.id, {
6873
path: path,
6974
findBy: {
7075
repositoryName: repoName,
7176
repositoryOwner: repoOwner,
72-
workflowRunId: latestRun.id,
77+
workflowRunId: run.id,
7378
token: ghToken,
7479
},
7580
});
7681
core.setOutput("artifact-download-path", response.downloadPath);
77-
core.setOutput("artifact-id", candidates[0].id);
78-
core.setOutput("workflow-run-id", latestRun.id);
82+
core.setOutput("artifact-id", foundArtifact.id);
83+
core.setOutput("workflow-run-id", run.id);
7984
}
8085

86+
async function getLatestArtifact(
87+
client,
88+
repoOwner,
89+
repoName,
90+
workflowId,
91+
headSha: string,
92+
status: string,
93+
artifactName: string,
94+
): Promise<{ artifact: any; run: any } | null> {
95+
const { data } = await client.rest.actions.listWorkflowRuns({
96+
repo: repoName,
97+
owner: repoOwner,
98+
head_sha: headSha,
99+
workflow_id: workflowId,
100+
status: status,
101+
});
102+
if (data.workflow_runs.length === 0) {
103+
console.log(`No ${status} runs found`);
104+
return null;
105+
}
106+
const run = data.workflow_runs[0];
107+
// Since these are pending workflows, the artifact might not be there yet. For this scenario, let's try this a couple of times:
108+
for (let attempt = 0; attempt < 5; attempt++) {
109+
const { data: artifacts } =
110+
await client.rest.actions.listWorkflowRunArtifacts({
111+
owner: repoOwner,
112+
repo: repoName,
113+
run_id: run.id,
114+
});
115+
const artifact = artifacts.artifacts.find(
116+
(art) => art.name == artifactName,
117+
);
118+
if (artifact) {
119+
console.log(`Found ${status} artifact`);
120+
return { artifact, run };
121+
}
122+
console.log("No artifact found, retrying in 10s");
123+
await new Promise((resolve) => {
124+
setTimeout(() => resolve(), 10000);
125+
});
126+
}
127+
}
81128
main();

0 commit comments

Comments
 (0)