-
Notifications
You must be signed in to change notification settings - Fork 64
/
Copy pathmain.js
177 lines (154 loc) · 4.98 KB
/
main.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
import pRetry from "p-retry";
// @ts-check
/**
* @param {string} appId
* @param {string} privateKey
* @param {string} owner
* @param {string[]} repositories
* @param {import("@actions/core")} core
* @param {import("@octokit/auth-app").createAppAuth} createAppAuth
* @param {import("@octokit/request").request} request
* @param {boolean} skipTokenRevoke
*/
export async function main(
appId,
privateKey,
owner,
repositories,
core,
createAppAuth,
request,
skipTokenRevoke
) {
let parsedOwner = "";
let parsedRepositoryNames = [];
// If neither owner nor repositories are set, default to current repository
if (!owner && repositories.length === 0) {
const [owner, repo] = String(process.env.GITHUB_REPOSITORY).split("/");
parsedOwner = owner;
parsedRepositoryNames = [repo];
core.info(
`owner and repositories not set, creating token for the current repository ("${repo}")`
);
}
// If only an owner is set, default to all repositories from that owner
if (owner && repositories.length === 0) {
parsedOwner = owner;
core.info(
`repositories not set, creating token for all repositories for given owner "${owner}"`
);
}
// If repositories are set, but no owner, default to `GITHUB_REPOSITORY_OWNER`
if (!owner && repositories.length > 0) {
parsedOwner = String(process.env.GITHUB_REPOSITORY_OWNER);
parsedRepositoryNames = repositories;
core.info(
`owner not set, creating owner for given repositories "${repositories.join(
","
)}" in current owner ("${parsedOwner}")`
);
}
// If both owner and repositories are set, use those values
if (owner && repositories.length > 0) {
parsedOwner = owner;
parsedRepositoryNames = repositories;
core.info(
`owner and repositories set, creating token for repositories "${repositories.join(
","
)}" owned by "${owner}"`
);
}
const auth = createAppAuth({
appId,
privateKey,
request,
});
let authentication, installationId, appSlug;
// If at least one repository is set, get installation ID from that repository
if (parsedRepositoryNames.length > 0) {
({ authentication, installationId, appSlug } = await pRetry(
() =>
getTokenFromRepository(
request,
auth,
parsedOwner,
parsedRepositoryNames
),
{
onFailedAttempt: (error) => {
core.info(
`Failed to create token for "${parsedRepositoryNames.join(
","
)}" (attempt ${error.attemptNumber}): ${error.message}`
);
},
retries: 3,
}
));
} else {
// Otherwise get the installation for the owner, which can either be an organization or a user account
({ authentication, installationId, appSlug } = await pRetry(
() => getTokenFromOwner(request, auth, parsedOwner),
{
onFailedAttempt: (error) => {
core.info(
`Failed to create token for "${parsedOwner}" (attempt ${error.attemptNumber}): ${error.message}`
);
},
retries: 3,
}
));
}
// Register the token with the runner as a secret to ensure it is masked in logs
core.setSecret(authentication.token);
core.setOutput("token", authentication.token);
core.setOutput("installation-id", installationId);
core.setOutput("app-slug", appSlug);
// Make token accessible to post function (so we can invalidate it)
if (!skipTokenRevoke) {
core.saveState("token", authentication.token);
core.saveState("expiresAt", authentication.expiresAt);
}
}
async function getTokenFromOwner(request, auth, parsedOwner) {
// https://docs.github.com/rest/apps/apps?apiVersion=2022-11-28#get-a-user-installation-for-the-authenticated-app
// This endpoint works for both users and organizations
const response = await request("GET /users/{username}/installation", {
username: parsedOwner,
request: {
hook: auth.hook,
},
});
// Get token for for all repositories of the given installation
const authentication = await auth({
type: "installation",
installationId: response.data.id,
});
const installationId = response.data.id;
const appSlug = response.data["app_slug"];
return { authentication, installationId, appSlug };
}
async function getTokenFromRepository(
request,
auth,
parsedOwner,
parsedRepositoryNames
) {
// https://docs.github.com/rest/apps/apps?apiVersion=2022-11-28#get-a-repository-installation-for-the-authenticated-app
const response = await request("GET /repos/{owner}/{repo}/installation", {
owner: parsedOwner,
repo: parsedRepositoryNames[0],
request: {
hook: auth.hook,
},
});
// Get token for given repositories
const authentication = await auth({
type: "installation",
installationId: response.data.id,
repositoryNames: parsedRepositoryNames,
});
const installationId = response.data.id;
const appSlug = response.data["app_slug"];
return { authentication, installationId, appSlug };
}