Skip to content

Commit babaff4

Browse files
authored
feat(outputs): app-slug and installation-id (actions#105)
It is convenient to use `https://api.github.com/users/$app_slug[bot]` to obtain the corresponding account ID later. Then build `Signed-off-by: $app_slug[bot] <$id+$app_slug[bot]@users.noreply.github.com>`. Currently, there is no Linux environment to build test snapshot files
1 parent bf627a5 commit babaff4

13 files changed

+98
-28
lines changed

README.md

+8
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,14 @@ jobs:
239239

240240
GitHub App installation access token.
241241

242+
### `installation-id`
243+
244+
GitHub App installation ID.
245+
246+
### `app-slug`
247+
248+
GitHub App slug.
249+
242250
## How it works
243251

244252
The action creates an installation access token using [the `POST /app/installations/{installation_id}/access_tokens` endpoint](https://docs.github.com/rest/apps/apps?apiVersion=2022-11-28#create-an-installation-access-token-for-an-app). By default,

action.yml

+4
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,10 @@ inputs:
4040
outputs:
4141
token:
4242
description: "GitHub installation access token"
43+
installation-id:
44+
description: "GitHub App installation ID"
45+
app-slug:
46+
description: "GitHub App slug"
4347
runs:
4448
using: "node20"
4549
main: "dist/main.cjs"

dist/main.cjs

+13-7
Original file line numberDiff line numberDiff line change
@@ -29923,28 +29923,30 @@ async function main(appId2, privateKey2, owner2, repositories2, core3, createApp
2992329923
privateKey: privateKey2,
2992429924
request: request2
2992529925
});
29926-
let authentication;
29926+
let authentication, installationId, appSlug;
2992729927
if (parsedRepositoryNames) {
29928-
authentication = await pRetry(() => getTokenFromRepository(request2, auth, parsedOwner, parsedRepositoryNames), {
29928+
({ authentication, installationId, appSlug } = await pRetry(() => getTokenFromRepository(request2, auth, parsedOwner, parsedRepositoryNames), {
2992929929
onFailedAttempt: (error) => {
2993029930
core3.info(
2993129931
`Failed to create token for "${parsedRepositoryNames}" (attempt ${error.attemptNumber}): ${error.message}`
2993229932
);
2993329933
},
2993429934
retries: 3
29935-
});
29935+
}));
2993629936
} else {
29937-
authentication = await pRetry(() => getTokenFromOwner(request2, auth, parsedOwner), {
29937+
({ authentication, installationId, appSlug } = await pRetry(() => getTokenFromOwner(request2, auth, parsedOwner), {
2993829938
onFailedAttempt: (error) => {
2993929939
core3.info(
2994029940
`Failed to create token for "${parsedOwner}" (attempt ${error.attemptNumber}): ${error.message}`
2994129941
);
2994229942
},
2994329943
retries: 3
29944-
});
29944+
}));
2994529945
}
2994629946
core3.setSecret(authentication.token);
2994729947
core3.setOutput("token", authentication.token);
29948+
core3.setOutput("installation-id", installationId);
29949+
core3.setOutput("app-slug", appSlug);
2994829950
if (!skipTokenRevoke2) {
2994929951
core3.saveState("token", authentication.token);
2995029952
core3.setOutput("expiresAt", authentication.expiresAt);
@@ -29970,7 +29972,9 @@ async function getTokenFromOwner(request2, auth, parsedOwner) {
2997029972
type: "installation",
2997129973
installationId: response.data.id
2997229974
});
29973-
return authentication;
29975+
const installationId = response.data.id;
29976+
const appSlug = response.data["app_slug"];
29977+
return { authentication, installationId, appSlug };
2997429978
}
2997529979
async function getTokenFromRepository(request2, auth, parsedOwner, parsedRepositoryNames) {
2997629980
const response = await request2("GET /repos/{owner}/{repo}/installation", {
@@ -29985,7 +29989,9 @@ async function getTokenFromRepository(request2, auth, parsedOwner, parsedReposit
2998529989
installationId: response.data.id,
2998629990
repositoryNames: parsedRepositoryNames.split(",")
2998729991
});
29988-
return authentication;
29992+
const installationId = response.data.id;
29993+
const appSlug = response.data["app_slug"];
29994+
return { authentication, installationId, appSlug };
2998929995
}
2999029996

2999129997
// lib/request.js

lib/main.js

+17-9
Original file line numberDiff line numberDiff line change
@@ -70,35 +70,36 @@ export async function main(
7070
request,
7171
});
7272

73-
let authentication;
73+
let authentication, installationId, appSlug;
7474
// If at least one repository is set, get installation ID from that repository
7575

7676
if (parsedRepositoryNames) {
77-
authentication = await pRetry(() => getTokenFromRepository(request, auth, parsedOwner, parsedRepositoryNames), {
77+
({ authentication, installationId, appSlug } = await pRetry(() => getTokenFromRepository(request, auth, parsedOwner, parsedRepositoryNames), {
7878
onFailedAttempt: (error) => {
7979
core.info(
8080
`Failed to create token for "${parsedRepositoryNames}" (attempt ${error.attemptNumber}): ${error.message}`
8181
);
8282
},
8383
retries: 3,
84-
});
85-
84+
}));
8685
} else {
8786
// Otherwise get the installation for the owner, which can either be an organization or a user account
88-
authentication = await pRetry(() => getTokenFromOwner(request, auth, parsedOwner), {
87+
({ authentication, installationId, appSlug } = await pRetry(() => getTokenFromOwner(request, auth, parsedOwner), {
8988
onFailedAttempt: (error) => {
9089
core.info(
9190
`Failed to create token for "${parsedOwner}" (attempt ${error.attemptNumber}): ${error.message}`
9291
);
9392
},
9493
retries: 3,
95-
});
94+
}));
9695
}
9796

9897
// Register the token with the runner as a secret to ensure it is masked in logs
9998
core.setSecret(authentication.token);
10099

101100
core.setOutput("token", authentication.token);
101+
core.setOutput("installation-id", installationId);
102+
core.setOutput("app-slug", appSlug);
102103

103104
// Make token accessible to post function (so we can invalidate it)
104105
if (!skipTokenRevoke) {
@@ -132,7 +133,11 @@ async function getTokenFromOwner(request, auth, parsedOwner) {
132133
type: "installation",
133134
installationId: response.data.id,
134135
});
135-
return authentication;
136+
137+
const installationId = response.data.id;
138+
const appSlug = response.data['app_slug'];
139+
140+
return { authentication, installationId, appSlug };
136141
}
137142

138143
async function getTokenFromRepository(request, auth, parsedOwner, parsedRepositoryNames) {
@@ -152,5 +157,8 @@ async function getTokenFromRepository(request, auth, parsedOwner, parsedReposito
152157
repositoryNames: parsedRepositoryNames.split(","),
153158
});
154159

155-
return authentication;
156-
}
160+
const installationId = response.data.id;
161+
const appSlug = response.data['app_slug'];
162+
163+
return { authentication, installationId, appSlug };
164+
}

tests/main-repo-skew.js

+3-1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ await test((mockPool) => {
99
const owner = process.env.INPUT_OWNER
1010
const repo = process.env.INPUT_REPOSITORIES
1111
const mockInstallationId = "123456";
12+
const mockAppSlug = "github-actions";
1213

1314
install({ now: 0, toFake: ["Date"] });
1415

@@ -44,7 +45,8 @@ await test((mockPool) => {
4445
return {
4546
statusCode: 200,
4647
data: {
47-
id: mockInstallationId
48+
id: mockInstallationId,
49+
"app_slug": mockAppSlug
4850
},
4951
responseOptions: {
5052
headers: {

tests/main-token-get-owner-set-repo-fail-response.test.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ await test((mockPool) => {
77
const owner = process.env.INPUT_OWNER;
88
const repo = process.env.INPUT_REPOSITORIES;
99
const mockInstallationId = "123456";
10+
const mockAppSlug = "github-actions";
1011

1112
mockPool
1213
.intercept({
@@ -32,7 +33,7 @@ await test((mockPool) => {
3233
})
3334
.reply(
3435
200,
35-
{ id: mockInstallationId },
36+
{ id: mockInstallationId, "app_slug": mockAppSlug },
3637
{ headers: { "content-type": "application/json" } }
3738
);
3839
});

tests/main-token-get-owner-set-to-org-repo-unset.test.js

+3-2
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,9 @@ await test((mockPool) => {
55
process.env.INPUT_OWNER = process.env.GITHUB_REPOSITORY_OWNER;
66
delete process.env.INPUT_REPOSITORIES;
77

8-
// Mock installation id request
8+
// Mock installation id and app slug request
99
const mockInstallationId = "123456";
10+
const mockAppSlug = "github-actions";
1011
mockPool
1112
.intercept({
1213
path: `/orgs/${process.env.INPUT_OWNER}/installation`,
@@ -19,7 +20,7 @@ await test((mockPool) => {
1920
})
2021
.reply(
2122
200,
22-
{ id: mockInstallationId },
23+
{ id: mockInstallationId, "app_slug": mockAppSlug },
2324
{ headers: { "content-type": "application/json" } }
2425
);
2526
});

tests/main-token-get-owner-set-to-user-fail-response.test.js

+3-2
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,9 @@ await test((mockPool) => {
55
process.env.INPUT_OWNER = "smockle";
66
delete process.env.INPUT_REPOSITORIES;
77

8-
// Mock installation id request
8+
// Mock installation ID and app slug request
99
const mockInstallationId = "123456";
10+
const mockAppSlug = "github-actions";
1011
mockPool
1112
.intercept({
1213
path: `/orgs/${process.env.INPUT_OWNER}/installation`,
@@ -30,7 +31,7 @@ await test((mockPool) => {
3031
})
3132
.reply(
3233
200,
33-
{ id: mockInstallationId },
34+
{ id: mockInstallationId, "app_slug": mockAppSlug },
3435
{ headers: { "content-type": "application/json" } }
3536
);
3637
});

tests/main-token-get-owner-set-to-user-repo-unset.test.js

+3-2
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,9 @@ await test((mockPool) => {
55
process.env.INPUT_OWNER = "smockle";
66
delete process.env.INPUT_REPOSITORIES;
77

8-
// Mock installation id request
8+
// Mock installation ID and app slug request
99
const mockInstallationId = "123456";
10+
const mockAppSlug = "github-actions";
1011
mockPool
1112
.intercept({
1213
path: `/orgs/${process.env.INPUT_OWNER}/installation`,
@@ -30,7 +31,7 @@ await test((mockPool) => {
3031
})
3132
.reply(
3233
200,
33-
{ id: mockInstallationId },
34+
{ id: mockInstallationId, "app_slug": mockAppSlug },
3435
{ headers: { "content-type": "application/json" } }
3536
);
3637
});

tests/main-token-get-owner-unset-repo-unset.test.js

+3-2
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,9 @@ await test((mockPool) => {
55
delete process.env.INPUT_OWNER;
66
delete process.env.INPUT_REPOSITORIES;
77

8-
// Mock installation id request
8+
// Mock installation ID and app slug request
99
const mockInstallationId = "123456";
10+
const mockAppSlug = "github-actions";
1011
mockPool
1112
.intercept({
1213
path: `/repos/${process.env.GITHUB_REPOSITORY}/installation`,
@@ -19,7 +20,7 @@ await test((mockPool) => {
1920
})
2021
.reply(
2122
200,
22-
{ id: mockInstallationId },
23+
{ id: mockInstallationId, "app_slug": mockAppSlug },
2324
{ headers: { "content-type": "application/json" } }
2425
);
2526
});

tests/main.js

+3-2
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,9 @@ export async function test(cb = (_mockPool) => {}, env = DEFAULT_ENV) {
5454

5555
// Calling `auth({ type: "app" })` to obtain a JWT doesn’t make network requests, so no need to intercept.
5656

57-
// Mock installation id request
57+
// Mock installation ID and app slug request
5858
const mockInstallationId = "123456";
59+
const mockAppSlug = "github-actions";
5960
const owner = env.INPUT_OWNER ?? env.GITHUB_REPOSITORY_OWNER;
6061
const repo = encodeURIComponent(
6162
(env.INPUT_REPOSITORIES ?? env.GITHUB_REPOSITORY).split(",")[0]
@@ -72,7 +73,7 @@ export async function test(cb = (_mockPool) => {}, env = DEFAULT_ENV) {
7273
})
7374
.reply(
7475
200,
75-
{ id: mockInstallationId },
76+
{ id: mockInstallationId, "app_slug": mockAppSlug },
7677
{ headers: { "content-type": "application/json" } }
7778
);
7879

tests/snapshots/index.js.md

+36
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,10 @@ Generated by [AVA](https://avajs.dev).
2828
::add-mask::ghs_16C7e42F292c6912E7710c838347Ae178B4a␊
2929
3030
::set-output name=token::ghs_16C7e42F292c6912E7710c838347Ae178B4a␊
31+
32+
::set-output name=installation-id::123456␊
33+
34+
::set-output name=app-slug::github-actions␊
3135
::save-state name=token::ghs_16C7e42F292c6912E7710c838347Ae178B4a␊
3236
3337
::set-output name=expiresAt::2016-07-11T22:14:10Z`
@@ -85,6 +89,10 @@ Generated by [AVA](https://avajs.dev).
8589
::add-mask::ghs_16C7e42F292c6912E7710c838347Ae178B4a␊
8690
8791
::set-output name=token::ghs_16C7e42F292c6912E7710c838347Ae178B4a␊
92+
93+
::set-output name=installation-id::123456␊
94+
95+
::set-output name=app-slug::github-actions␊
8896
::save-state name=token::ghs_16C7e42F292c6912E7710c838347Ae178B4a␊
8997
9098
::set-output name=expiresAt::2016-07-11T22:14:10Z`
@@ -101,6 +109,10 @@ Generated by [AVA](https://avajs.dev).
101109
::add-mask::ghs_16C7e42F292c6912E7710c838347Ae178B4a␊
102110
103111
::set-output name=token::ghs_16C7e42F292c6912E7710c838347Ae178B4a␊
112+
113+
::set-output name=installation-id::123456␊
114+
115+
::set-output name=app-slug::github-actions␊
104116
::save-state name=token::ghs_16C7e42F292c6912E7710c838347Ae178B4a␊
105117
106118
::set-output name=expiresAt::2016-07-11T22:14:10Z`
@@ -117,6 +129,10 @@ Generated by [AVA](https://avajs.dev).
117129
::add-mask::ghs_16C7e42F292c6912E7710c838347Ae178B4a␊
118130
119131
::set-output name=token::ghs_16C7e42F292c6912E7710c838347Ae178B4a␊
132+
133+
::set-output name=installation-id::123456␊
134+
135+
::set-output name=app-slug::github-actions␊
120136
::save-state name=token::ghs_16C7e42F292c6912E7710c838347Ae178B4a␊
121137
122138
::set-output name=expiresAt::2016-07-11T22:14:10Z`
@@ -133,6 +149,10 @@ Generated by [AVA](https://avajs.dev).
133149
::add-mask::ghs_16C7e42F292c6912E7710c838347Ae178B4a␊
134150
135151
::set-output name=token::ghs_16C7e42F292c6912E7710c838347Ae178B4a␊
152+
153+
::set-output name=installation-id::123456␊
154+
155+
::set-output name=app-slug::github-actions␊
136156
::save-state name=token::ghs_16C7e42F292c6912E7710c838347Ae178B4a␊
137157
138158
::set-output name=expiresAt::2016-07-11T22:14:10Z`
@@ -150,6 +170,10 @@ Generated by [AVA](https://avajs.dev).
150170
::add-mask::ghs_16C7e42F292c6912E7710c838347Ae178B4a␊
151171
152172
::set-output name=token::ghs_16C7e42F292c6912E7710c838347Ae178B4a␊
173+
174+
::set-output name=installation-id::123456␊
175+
176+
::set-output name=app-slug::github-actions␊
153177
::save-state name=token::ghs_16C7e42F292c6912E7710c838347Ae178B4a␊
154178
155179
::set-output name=expiresAt::2016-07-11T22:14:10Z`
@@ -166,6 +190,10 @@ Generated by [AVA](https://avajs.dev).
166190
::add-mask::ghs_16C7e42F292c6912E7710c838347Ae178B4a␊
167191
168192
::set-output name=token::ghs_16C7e42F292c6912E7710c838347Ae178B4a␊
193+
194+
::set-output name=installation-id::123456␊
195+
196+
::set-output name=app-slug::github-actions␊
169197
::save-state name=token::ghs_16C7e42F292c6912E7710c838347Ae178B4a␊
170198
171199
::set-output name=expiresAt::2016-07-11T22:14:10Z`
@@ -182,6 +210,10 @@ Generated by [AVA](https://avajs.dev).
182210
::add-mask::ghs_16C7e42F292c6912E7710c838347Ae178B4a␊
183211
184212
::set-output name=token::ghs_16C7e42F292c6912E7710c838347Ae178B4a␊
213+
214+
::set-output name=installation-id::123456␊
215+
216+
::set-output name=app-slug::github-actions␊
185217
::save-state name=token::ghs_16C7e42F292c6912E7710c838347Ae178B4a␊
186218
187219
::set-output name=expiresAt::2016-07-11T22:14:10Z`
@@ -198,6 +230,10 @@ Generated by [AVA](https://avajs.dev).
198230
::add-mask::ghs_16C7e42F292c6912E7710c838347Ae178B4a␊
199231
200232
::set-output name=token::ghs_16C7e42F292c6912E7710c838347Ae178B4a␊
233+
234+
::set-output name=installation-id::123456␊
235+
236+
::set-output name=app-slug::github-actions␊
201237
::save-state name=token::ghs_16C7e42F292c6912E7710c838347Ae178B4a␊
202238
203239
::set-output name=expiresAt::2016-07-11T22:14:10Z`

tests/snapshots/index.js.snap

50 Bytes
Binary file not shown.

0 commit comments

Comments
 (0)