Files
create-github-app-token/lib/main.js
T
dependabot[bot] dce3be8b28 fix(deps): bump p-retry from 6.2.1 to 7.1.0 (#294)
Bumps [p-retry](https://github.com/sindresorhus/p-retry) from 6.2.1 to
7.1.0.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/sindresorhus/p-retry/releases">p-retry's
releases</a>.</em></p>
<blockquote>
<h2>v7.0.0</h2>
<h3>Breaking</h3>
<ul>
<li>Require Node.js 20  3bdb53a</li>
<li><code>onFailedAttempt</code> and <code>shouldRetry</code> now
receive a <code>context</code> object instead of a decorated
<code>error</code> bff36bb
<ul>
<li>You must now must access the error as <code>object.error</code>
instead of <code>object</code>.</li>
<li>The use of <code>.attemptNumber</code> and <code>.retriesLeft</code>
did not change.</li>
</ul>
</li>
<li>Remove the <code>forever</code> option (<a
href="https://redirect.github.com/sindresorhus/p-retry/issues/79">#79</a>)
6a89827
<ul>
<li>Many use-cases can use <code>{retries: Infinity}</code> instead for
infinite retries.</li>
</ul>
</li>
</ul>
<h3>Improvements</h3>
<ul>
<li>Rewrite the package to not depend on the <code>retry</code> package
(<a
href="https://redirect.github.com/sindresorhus/p-retry/issues/79">#79</a>)
6a89827
<ul>
<li>This is a full rewrite, so test carefully.</li>
</ul>
</li>
<li>Add <a
href="https://github.com/sindresorhus/p-retry#makeretriablefunction-options"><code>makeRetriable</code></a>
method 1a81c1e</li>
</ul>
<hr />
<p><a
href="https://github.com/sindresorhus/p-retry/compare/v6.2.1...v7.0.0">https://github.com/sindresorhus/p-retry/compare/v6.2.1...v7.0.0</a></p>
<h2>v7.0.0-0</h2>
<h3>Breaking</h3>
<ul>
<li>Require Node.js 20  3bdb53a</li>
<li><code>onFailedAttempt</code> and <code>shouldRetry</code> now
receive a <code>context</code> object instead of a decorated
<code>error</code> bff36bb
<ul>
<li>You must now must access the error as <code>object.error</code>
instead of <code>object</code>.</li>
<li>The use of <code>.attemptNumber</code> and <code>.retriesLeft</code>
did not change.</li>
</ul>
</li>
<li>Remove the <code>forever</code> option (<a
href="https://redirect.github.com/sindresorhus/p-retry/issues/79">#79</a>)
6a89827
<ul>
<li>Many use-cases can use <code>{retries: Infinity}</code> instead for
infinite retries.</li>
</ul>
</li>
</ul>
<h3>Improvements</h3>
<ul>
<li>Rewrite the package to not depend on the <code>retry</code> package
(<a
href="https://redirect.github.com/sindresorhus/p-retry/issues/79">#79</a>)
6a89827
<ul>
<li>This is a full rewrite, so test carefully.</li>
</ul>
</li>
<li>Add <a
href="https://github.com/sindresorhus/p-retry?tab=readme-ov-file#makeretriablefunction-options"><code>makeRetriable</code></a>
method 1a81c1e</li>
</ul>
<hr />
<p><a
href="https://github.com/sindresorhus/p-retry/compare/v6.2.1...v7.0.0-0">https://github.com/sindresorhus/p-retry/compare/v6.2.1...v7.0.0-0</a></p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/sindresorhus/p-retry/commit/e11ca9c9c4a8032182403871593a67a49e948568"><code>e11ca9c</code></a>
7.0.0</li>
<li><a
href="https://github.com/sindresorhus/p-retry/commit/81608ce107e17b388721ec6377b58e29c52ea55c"><code>81608ce</code></a>
Minor tweaks</li>
<li><a
href="https://github.com/sindresorhus/p-retry/commit/9ef6a73d71c875c06de0090774806eb5ef1b4a7d"><code>9ef6a73</code></a>
Clarify <code>shouldRetry</code> documentation (<a
href="https://redirect.github.com/sindresorhus/p-retry/issues/93">#93</a>)</li>
<li><a
href="https://github.com/sindresorhus/p-retry/commit/e05b1017e183db72962fbc8edb5fa1c86d33bc3c"><code>e05b101</code></a>
Fix code example typo (<a
href="https://redirect.github.com/sindresorhus/p-retry/issues/91">#91</a>)</li>
<li><a
href="https://github.com/sindresorhus/p-retry/commit/157d06eabbcae7066e8cbf430ad0da759a4ca9a1"><code>157d06e</code></a>
7.0.0-0</li>
<li><a
href="https://github.com/sindresorhus/p-retry/commit/bff36bb860a8ea8551982cd6bce5a2aaed4135fb"><code>bff36bb</code></a>
Stop decorating errors</li>
<li><a
href="https://github.com/sindresorhus/p-retry/commit/1a81c1e2d25718119d59ce33f9feeceb47a7436a"><code>1a81c1e</code></a>
Add <code>makeRetriable</code> method</li>
<li><a
href="https://github.com/sindresorhus/p-retry/commit/481105165809e0dd47679f5f576a0734bce7bcb9"><code>4811051</code></a>
Document mocking</li>
<li><a
href="https://github.com/sindresorhus/p-retry/commit/777d98f331affb8782eb50bee7d48e7c825224ca"><code>777d98f</code></a>
Document signal handling</li>
<li><a
href="https://github.com/sindresorhus/p-retry/commit/b4e52fcafab4592d353e1901c697f49605193496"><code>b4e52fc</code></a>
Add test for async stack traces</li>
<li>Additional commits viewable in <a
href="https://github.com/sindresorhus/p-retry/compare/v6.2.1...v7.0.0">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=p-retry&package-manager=npm_and_yarn&previous-version=6.2.1&new-version=7.0.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

You can trigger a rebase of this PR by commenting `@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

> **Note**
> Automatic rebases have been disabled on this pull request as it has
been open for over 30 days.

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Parker Brown <17183625+parkerbxyz@users.noreply.github.com>
2025-11-21 15:50:07 -08:00

184 lines
5.3 KiB
JavaScript

import pRetry from "p-retry";
// @ts-check
/**
* @param {string} appId
* @param {string} privateKey
* @param {string} owner
* @param {string[]} repositories
* @param {undefined | Record<string, string>} permissions
* @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,
permissions,
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(
`Inputs 'owner' and 'repositories' are not set. Creating token for this repository (${owner}/${repo}).`
);
}
// If only an owner is set, default to all repositories from that owner
if (owner && repositories.length === 0) {
parsedOwner = owner;
core.info(
`Input 'repositories' is not set. Creating token for all repositories owned by ${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(
`No 'owner' input provided. Using default owner '${parsedOwner}' to create token for the following repositories:${repositories
.map((repo) => `\n- ${parsedOwner}/${repo}`)
.join("")}`
);
}
// If both owner and repositories are set, use those values
if (owner && repositories.length > 0) {
parsedOwner = owner;
parsedRepositoryNames = repositories;
core.info(
`Inputs 'owner' and 'repositories' are set. Creating token for the following repositories:
${repositories.map((repo) => `\n- ${parsedOwner}/${repo}`).join("")}`
);
}
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,
permissions
),
{
shouldRetry: ({ error }) => error.status >= 500,
onFailedAttempt: (context) => {
core.info(
`Failed to create token for "${parsedRepositoryNames.join(
","
)}" (attempt ${context.attemptNumber}): ${context.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, permissions),
{
onFailedAttempt: (context) => {
core.info(
`Failed to create token for "${parsedOwner}" (attempt ${context.attemptNumber}): ${context.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, permissions) {
// 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,
permissions,
});
const installationId = response.data.id;
const appSlug = response.data["app_slug"];
return { authentication, installationId, appSlug };
}
async function getTokenFromRepository(
request,
auth,
parsedOwner,
parsedRepositoryNames,
permissions
) {
// 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,
permissions,
});
const installationId = response.data.id;
const appSlug = response.data["app_slug"];
return { authentication, installationId, appSlug };
}