Skip to content

Commit 9a36195

Browse files
authored
feat: new get-latest-workflow-artifact action (#988)
This allows the user to retrieve an artifact from the latest run of a specific workflow.
1 parent bfed586 commit 9a36195

File tree

9 files changed

+611
-0
lines changed

9 files changed

+611
-0
lines changed
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
1.2.13
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
node_modules
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# get-latest-workflow-artifact
2+
3+
This action can be used to download the latest artifact of a specific workflow within the context of a pull request.
4+
One use-case for this is to access a report generated by another workflow.
5+
6+
Required permissions:
7+
8+
- `pull-requests: read`
9+
- `actions: read`
10+
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+
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 |
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
name: get-latest-workflow-artifact
2+
description: Retrieve a specific artifact of the latest run of a workflow for a pull request
3+
inputs:
4+
artifact-name:
5+
description: Name of a specific artifact
6+
required: true
7+
workflow-id:
8+
description: ID of the workflow inside the current repository
9+
required: true
10+
repository:
11+
description: Repository of the target workflow (e.g. `grafana/grafana`)
12+
required: false
13+
default: ${{ github.repository }}
14+
pr-number:
15+
description: Pull request the workflow run is associated with
16+
required: false
17+
default: ${{ github.event.pull_request.number }}
18+
github-token:
19+
description: GitHub token to access the workflow and artifact
20+
required: false
21+
default: ${{ github.token }}
22+
path:
23+
description: Destination path
24+
required: false
25+
default: ${{ github.workspace }}
26+
27+
outputs:
28+
artifact-download-path:
29+
description: Path of the downloaded artifact
30+
value: ${{ steps.get-artifact.outputs.artifact-download-path }}
31+
artifact-id:
32+
description: ID of the downloaded artifact
33+
value: ${{ steps.get-artifact.outputs.artifact-id }}
34+
workflow-run-id:
35+
description: ID of the considered workflow run
36+
value: ${{ steps.get-artifact.outputs.workflow-run-id }}
37+
38+
runs:
39+
using: "composite"
40+
steps:
41+
- name: Install bun package manager
42+
uses: oven-sh/setup-bun@4bc047ad259df6fc24a6c9b0f9a0cb08cf17fbe5 # v2.0.1
43+
with:
44+
bun-version-file: ${{ github.action_path }}/.bun-version
45+
46+
- name: Install dependencies
47+
shell: bash
48+
working-directory: ${{ github.action_path }}
49+
run: |
50+
bun install --frozen-lockfile --production
51+
52+
- name: Get latest workflow artifact
53+
id: get-artifact
54+
shell: bash
55+
working-directory: ${{ github.action_path }}
56+
env:
57+
INPUT_GITHUB-TOKEN: ${{ inputs.github-token }}
58+
INPUT_ARTIFACT-NAME: ${{ inputs.artifact-name }}
59+
INPUT_WORKFLOW-ID: ${{ inputs.workflow-id }}
60+
INPUT_REPOSITORY: ${{ inputs.repository }}
61+
INPUT_PR-NUMBER: ${{ inputs.pr-number }}
62+
INPUT_PATH: ${{ inputs.path }}
63+
NODE_ENV: "production"
64+
run: |
65+
bun run src/main.ts

actions/get-latest-workflow-artifact/bun.lock

Lines changed: 388 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
{
2+
"name": "get-latest-workflow-artifact-action",
3+
"version": "1.0.0",
4+
"author": "",
5+
"repository": {
6+
"type": "git",
7+
"url": "git+https://github.com/grafana/get-latest-workflow-artifact-action.git"
8+
},
9+
"main": "dist/main.js",
10+
"dependencies": {
11+
"@actions/artifact": "^2.3.2",
12+
"@actions/core": "^1.11.1",
13+
"@actions/github": "^6.0.1"
14+
},
15+
"devDependencies": {
16+
"@types/bun": "latest",
17+
"@types/node": "^22.15.18"
18+
},
19+
"bugs": {
20+
"url": "https://github.com/grafana/get-latest-workflow-artifact-action/issues"
21+
},
22+
"description": "",
23+
"homepage": "https://github.com/grafana/get-latest-workflow-artifact-action#readme",
24+
"license": "ISC",
25+
"private": true,
26+
"scripts": {}
27+
}
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
import * as core from "@actions/core";
2+
import * as github from "@actions/github";
3+
import { DefaultArtifactClient } from "@actions/artifact";
4+
import * as filepath from "node:path";
5+
6+
async function main() {
7+
const ghToken = core.getInput("github-token", { required: true });
8+
const workflowId = core.getInput("workflow-id", { required: true });
9+
const [repoOwner, repoName] = core
10+
.getInput("repository", { required: true })
11+
.split("/");
12+
const prNumber = parseInt(core.getInput("pr-number", { required: true }), 10);
13+
const artifactName = core.getInput("artifact-name", { required: true });
14+
let path = core.getInput("path", { required: false });
15+
16+
if (!path) {
17+
const workspace = process.env.GITHUB_WORKSPACE || process.cwd();
18+
if (!workspace) {
19+
throw new Error("GITHUB_WORKSPACE is not set");
20+
}
21+
path = workspace;
22+
}
23+
24+
path = filepath.join(path, artifactName);
25+
26+
const client = github.getOctokit(ghToken);
27+
28+
const { data } = await client.rest.pulls.get({
29+
owner: repoOwner,
30+
repo: repoName,
31+
pull_number: prNumber,
32+
});
33+
34+
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+
});
43+
44+
if (runs.workflow_runs.length <= 0) {
45+
throw new Error(`No workflow runs found for sha ${headSha}`);
46+
}
47+
48+
const latestRun = runs.workflow_runs[0];
49+
core.info(`Latest run: ${latestRun.id} <${latestRun.html_url}>`);
50+
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+
});
58+
59+
const artifact = new DefaultArtifactClient();
60+
const candidates = artifacts.artifacts.filter(
61+
(artifact) => artifact.name == artifactName,
62+
);
63+
if (candidates.length <= 0) {
64+
throw new Error(`No artifacts found with name ${artifactName}`);
65+
}
66+
67+
const response = await artifact.downloadArtifact(candidates[0].id, {
68+
path: path,
69+
findBy: {
70+
repositoryName: repoName,
71+
repositoryOwner: repoOwner,
72+
workflowRunId: latestRun.id,
73+
token: ghToken,
74+
},
75+
});
76+
core.setOutput("artifact-download-path", response.downloadPath);
77+
core.setOutput("artifact-id", candidates[0].id);
78+
core.setOutput("workflow-run-id", latestRun.id);
79+
}
80+
81+
main();

catalog-info.yaml

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,25 @@ spec:
138138
subcomponentOf: component:shared-workflows
139139
type: github-action
140140

141+
---
142+
apiVersion: backstage.io/v1alpha1
143+
kind: Component
144+
metadata:
145+
annotations:
146+
github.com/project-slug: grafana/shared-workflows
147+
description: Retrieve a specific artifact of the latest run of a workflow for a
148+
pull request
149+
links:
150+
- title: README
151+
url: https://github.com/grafana/shared-workflows/blob/main/actions/get-latest-workflow-artifact/README.md
152+
name: shared-workflows-get-latest-workflow-artifact
153+
title: get-latest-workflow-artifact
154+
spec:
155+
lifecycle: production
156+
owner: group:platform-productivity
157+
subcomponentOf: component:shared-workflows
158+
type: github-action
159+
141160
---
142161
apiVersion: backstage.io/v1alpha1
143162
kind: Component

release-please-config.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,11 @@
137137
"actions/dependabot-auto-triage": {
138138
"package-name": "dependabot-auto-triage",
139139
"extra-files": ["README.md"]
140+
},
141+
"actions/get-latest-workflow-artifact": {
142+
"package-name": "get-latest-workflow-artifact",
143+
"extra-files": ["README.md"],
144+
"initial-version": "0.1.0"
140145
}
141146
},
142147
"release-type": "simple",

0 commit comments

Comments
 (0)