Compare commits
14
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2156e19368 | ||
|
|
b24274086f | ||
|
|
14350b6aaa | ||
|
|
6cf7b5f22a | ||
|
|
22e6bc6b49 | ||
|
|
3b3f07c3d1 | ||
|
|
7b860611c2 | ||
|
|
a84c82dc20 | ||
|
|
81e8c224df | ||
|
|
7434028a6d | ||
|
|
46f9f788b8 | ||
|
|
3c69395e16 | ||
|
|
55b8c24e8d | ||
|
|
cbc2930e9b |
@@ -1,3 +1,4 @@
|
|||||||
.env
|
.env
|
||||||
coverage
|
coverage
|
||||||
node_modules/
|
node_modules/
|
||||||
|
.DS_Store
|
||||||
@@ -195,6 +195,28 @@ jobs:
|
|||||||
body: "Hello, World!"
|
body: "Hello, World!"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Create a token for an enterprise installation
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
on: [workflow_dispatch]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
hello-world:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/create-github-app-token@v2
|
||||||
|
id: app-token
|
||||||
|
with:
|
||||||
|
app-id: ${{ vars.APP_ID }}
|
||||||
|
private-key: ${{ secrets.PRIVATE_KEY }}
|
||||||
|
enterprise-slug: my-enterprise-slug
|
||||||
|
- name: Call enterprise management REST API with gh
|
||||||
|
run: |
|
||||||
|
gh api /enterprises/my-enterprise-slug/apps/installable_organizations
|
||||||
|
env:
|
||||||
|
GH_TOKEN: ${{ steps.app-token.outputs.token }}
|
||||||
|
```
|
||||||
|
|
||||||
### Create a token with specific permissions
|
### Create a token with specific permissions
|
||||||
|
|
||||||
> [!NOTE]
|
> [!NOTE]
|
||||||
@@ -335,6 +357,13 @@ steps:
|
|||||||
> [!NOTE]
|
> [!NOTE]
|
||||||
> If `owner` is set and `repositories` is empty, access will be scoped to all repositories in the provided repository owner's installation. If `owner` and `repositories` are empty, access will be scoped to only the current repository.
|
> If `owner` is set and `repositories` is empty, access will be scoped to all repositories in the provided repository owner's installation. If `owner` and `repositories` are empty, access will be scoped to only the current repository.
|
||||||
|
|
||||||
|
### `enterprise-slug`
|
||||||
|
|
||||||
|
**Optional:** The slug of the enterprise to generate a token for enterprise-level app installations.
|
||||||
|
|
||||||
|
> [!NOTE]
|
||||||
|
> The `enterprise-slug` input is mutually exclusive with `owner` and `repositories`. GitHub Apps can be installed on enterprise accounts with permissions that let them call enterprise management APIs. Enterprise installations do not grant access to organization or repository resources.
|
||||||
|
|
||||||
### `permission-<permission name>`
|
### `permission-<permission name>`
|
||||||
|
|
||||||
**Optional:** The permissions to grant to the token. By default, the token inherits all of the installation's permissions. We recommend to explicitly list the permissions that are required for a use case. This follows GitHub's own recommendation to [control permissions of `GITHUB_TOKEN` in workflows](https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/controlling-permissions-for-github_token). The documentation also lists all available permissions, just prefix the permission key with `permission-` (e.g., `pull-requests` → `permission-pull-requests`).
|
**Optional:** The permissions to grant to the token. By default, the token inherits all of the installation's permissions. We recommend to explicitly list the permissions that are required for a use case. This follows GitHub's own recommendation to [control permissions of `GITHUB_TOKEN` in workflows](https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/controlling-permissions-for-github_token). The documentation also lists all available permissions, just prefix the permission key with `permission-` (e.g., `pull-requests` → `permission-pull-requests`).
|
||||||
|
|||||||
@@ -17,6 +17,9 @@ inputs:
|
|||||||
repositories:
|
repositories:
|
||||||
description: "Comma or newline-separated list of repositories to install the GitHub App on (defaults to current repository if owner is unset)"
|
description: "Comma or newline-separated list of repositories to install the GitHub App on (defaults to current repository if owner is unset)"
|
||||||
required: false
|
required: false
|
||||||
|
enterprise-slug:
|
||||||
|
description: "Enterprise slug for enterprise-level app installations (cannot be used with 'owner' or 'repositories')"
|
||||||
|
required: false
|
||||||
skip-token-revoke:
|
skip-token-revoke:
|
||||||
description: "If true, the token will not be revoked when the current job is complete"
|
description: "If true, the token will not be revoked when the current job is complete"
|
||||||
required: false
|
required: false
|
||||||
|
|||||||
+97
-36
@@ -4,6 +4,7 @@ import pRetry from "p-retry";
|
|||||||
/**
|
/**
|
||||||
* @param {string} appId
|
* @param {string} appId
|
||||||
* @param {string} privateKey
|
* @param {string} privateKey
|
||||||
|
* @param {string} enterpriseSlug
|
||||||
* @param {string} owner
|
* @param {string} owner
|
||||||
* @param {string[]} repositories
|
* @param {string[]} repositories
|
||||||
* @param {undefined | Record<string, string>} permissions
|
* @param {undefined | Record<string, string>} permissions
|
||||||
@@ -15,58 +16,70 @@ import pRetry from "p-retry";
|
|||||||
export async function main(
|
export async function main(
|
||||||
appId,
|
appId,
|
||||||
privateKey,
|
privateKey,
|
||||||
|
enterpriseSlug,
|
||||||
owner,
|
owner,
|
||||||
repositories,
|
repositories,
|
||||||
permissions,
|
permissions,
|
||||||
core,
|
core,
|
||||||
createAppAuth,
|
createAppAuth,
|
||||||
request,
|
request,
|
||||||
skipTokenRevoke
|
skipTokenRevoke,
|
||||||
|
|
||||||
) {
|
) {
|
||||||
|
// Validate mutual exclusivity of enterprise-slug with owner/repositories
|
||||||
|
if (enterpriseSlug && (owner || repositories.length > 0)) {
|
||||||
|
throw new Error("Cannot use 'enterprise-slug' input with 'owner' or 'repositories' inputs");
|
||||||
|
}
|
||||||
|
|
||||||
let parsedOwner = "";
|
let parsedOwner = "";
|
||||||
let parsedRepositoryNames = [];
|
let parsedRepositoryNames = [];
|
||||||
|
|
||||||
// If neither owner nor repositories are set, default to current repository
|
// Skip owner/repository parsing if enterprise-slug is set
|
||||||
if (!owner && repositories.length === 0) {
|
if (!enterpriseSlug) {
|
||||||
const [owner, repo] = String(process.env.GITHUB_REPOSITORY).split("/");
|
// If neither owner nor repositories are set, default to current repository
|
||||||
parsedOwner = owner;
|
if (!owner && repositories.length === 0) {
|
||||||
parsedRepositoryNames = [repo];
|
const [owner, repo] = String(process.env.GITHUB_REPOSITORY).split("/");
|
||||||
|
parsedOwner = owner;
|
||||||
|
parsedRepositoryNames = [repo];
|
||||||
|
|
||||||
core.info(
|
core.info(
|
||||||
`Inputs 'owner' and 'repositories' are not set. Creating token for this repository (${owner}/${repo}).`
|
`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 only an owner is set, default to all repositories from that owner
|
||||||
if (owner && repositories.length === 0) {
|
if (owner && repositories.length === 0) {
|
||||||
parsedOwner = owner;
|
parsedOwner = owner;
|
||||||
|
|
||||||
core.info(
|
core.info(
|
||||||
`Input 'repositories' is not set. Creating token for all repositories owned by ${owner}.`
|
`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 repositories are set, but no owner, default to `GITHUB_REPOSITORY_OWNER`
|
||||||
if (!owner && repositories.length > 0) {
|
if (!owner && repositories.length > 0) {
|
||||||
parsedOwner = String(process.env.GITHUB_REPOSITORY_OWNER);
|
parsedOwner = String(process.env.GITHUB_REPOSITORY_OWNER);
|
||||||
parsedRepositoryNames = repositories;
|
parsedRepositoryNames = repositories;
|
||||||
|
|
||||||
core.info(
|
core.info(
|
||||||
`No 'owner' input provided. Using default owner '${parsedOwner}' to create token for the following repositories:${repositories
|
`No 'owner' input provided. Using default owner '${parsedOwner}' to create token for the following repositories:${repositories
|
||||||
.map((repo) => `\n- ${parsedOwner}/${repo}`)
|
.map((repo) => `\n- ${parsedOwner}/${repo}`)
|
||||||
.join("")}`
|
.join("")}`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// If both owner and repositories are set, use those values
|
// If both owner and repositories are set, use those values
|
||||||
if (owner && repositories.length > 0) {
|
if (owner && repositories.length > 0) {
|
||||||
parsedOwner = owner;
|
parsedOwner = owner;
|
||||||
parsedRepositoryNames = repositories;
|
parsedRepositoryNames = repositories;
|
||||||
|
|
||||||
core.info(
|
core.info(
|
||||||
`Inputs 'owner' and 'repositories' are set. Creating token for the following repositories:
|
`Inputs 'owner' and 'repositories' are set. Creating token for the following repositories:
|
||||||
${repositories.map((repo) => `\n- ${parsedOwner}/${repo}`).join("")}`
|
${repositories.map((repo) => `\n- ${parsedOwner}/${repo}`).join("")}`
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
core.info(`Creating enterprise installation token for enterprise "${enterpriseSlug}".`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const auth = createAppAuth({
|
const auth = createAppAuth({
|
||||||
@@ -76,9 +89,22 @@ export async function main(
|
|||||||
});
|
});
|
||||||
|
|
||||||
let authentication, installationId, appSlug;
|
let authentication, installationId, appSlug;
|
||||||
// If at least one repository is set, get installation ID from that repository
|
|
||||||
|
|
||||||
if (parsedRepositoryNames.length > 0) {
|
// If enterprise-slug is set, get installation ID from the enterprise
|
||||||
|
if (enterpriseSlug) {
|
||||||
|
({ authentication, installationId, appSlug } = await pRetry(
|
||||||
|
() => getTokenFromEnterprise(request, auth, enterpriseSlug, permissions),
|
||||||
|
{
|
||||||
|
shouldRetry: (error) => error.status >= 500,
|
||||||
|
onFailedAttempt: (error) => {
|
||||||
|
core.info(
|
||||||
|
`Failed to create token for enterprise "${enterpriseSlug}" (attempt ${error.attemptNumber}): ${error.message}`
|
||||||
|
);
|
||||||
|
},
|
||||||
|
retries: 3,
|
||||||
|
}
|
||||||
|
));
|
||||||
|
} else if (parsedRepositoryNames.length > 0) {
|
||||||
({ authentication, installationId, appSlug } = await pRetry(
|
({ authentication, installationId, appSlug } = await pRetry(
|
||||||
() =>
|
() =>
|
||||||
getTokenFromRepository(
|
getTokenFromRepository(
|
||||||
@@ -181,3 +207,38 @@ async function getTokenFromRepository(
|
|||||||
|
|
||||||
return { authentication, installationId, appSlug };
|
return { authentication, installationId, appSlug };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function getTokenFromEnterprise(request, auth, enterpriseSlug, permissions) {
|
||||||
|
// Get all installations and find the enterprise one
|
||||||
|
// https://docs.github.com/rest/apps/apps#list-installations-for-the-authenticated-app
|
||||||
|
// Note: Currently we do not have a way to get the installation for an enterprise directly,
|
||||||
|
// so as a workaround we need to list all installations and filter for the enterprise one.
|
||||||
|
const response = await request("GET /app/installations", {
|
||||||
|
request: {
|
||||||
|
hook: auth.hook,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// Find the enterprise installation
|
||||||
|
const enterpriseInstallation = response.data.find(
|
||||||
|
installation => installation.target_type === "Enterprise" &&
|
||||||
|
installation.account?.slug === enterpriseSlug
|
||||||
|
);
|
||||||
|
|
||||||
|
/* c8 ignore next 3 */
|
||||||
|
if (!enterpriseInstallation) {
|
||||||
|
throw new Error(`No enterprise installation found matching the name ${enterpriseSlug}. Available installations: ${response.data.map(i => `${i.target_type}:${i.account?.login || 'N/A'}`).join(', ')}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get token for the enterprise installation
|
||||||
|
const authentication = await auth({
|
||||||
|
type: "installation",
|
||||||
|
installationId: enterpriseInstallation.id,
|
||||||
|
permissions,
|
||||||
|
});
|
||||||
|
|
||||||
|
const installationId = enterpriseInstallation.id;
|
||||||
|
const appSlug = enterpriseInstallation["app_slug"];
|
||||||
|
|
||||||
|
return { authentication, installationId, appSlug };
|
||||||
|
}
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ if (!process.env.GITHUB_REPOSITORY_OWNER) {
|
|||||||
|
|
||||||
const appId = core.getInput("app-id");
|
const appId = core.getInput("app-id");
|
||||||
const privateKey = core.getInput("private-key");
|
const privateKey = core.getInput("private-key");
|
||||||
|
const enterpriseSlug = core.getInput("enterprise-slug");
|
||||||
const owner = core.getInput("owner");
|
const owner = core.getInput("owner");
|
||||||
const repositories = core
|
const repositories = core
|
||||||
.getInput("repositories")
|
.getInput("repositories")
|
||||||
@@ -32,6 +33,7 @@ const permissions = getPermissionsFromInputs(process.env);
|
|||||||
export default main(
|
export default main(
|
||||||
appId,
|
appId,
|
||||||
privateKey,
|
privateKey,
|
||||||
|
enterpriseSlug,
|
||||||
owner,
|
owner,
|
||||||
repositories,
|
repositories,
|
||||||
permissions,
|
permissions,
|
||||||
@@ -40,7 +42,10 @@ export default main(
|
|||||||
request,
|
request,
|
||||||
skipTokenRevoke,
|
skipTokenRevoke,
|
||||||
).catch((error) => {
|
).catch((error) => {
|
||||||
/* c8 ignore next 3 */
|
/* c8 ignore next 5 */
|
||||||
console.error(error);
|
console.error(error);
|
||||||
core.setFailed(error.message);
|
// Don't set failed in test mode (when GITHUB_OUTPUT is undefined)
|
||||||
|
if (process.env.GITHUB_OUTPUT !== undefined) {
|
||||||
|
core.setFailed(error.message);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -0,0 +1,40 @@
|
|||||||
|
import { test } from "./main.js";
|
||||||
|
|
||||||
|
|
||||||
|
// Verify `main` handles when no enterprise installation is found.
|
||||||
|
await test((mockPool) => {
|
||||||
|
delete process.env.INPUT_OWNER;
|
||||||
|
delete process.env.INPUT_REPOSITORIES;
|
||||||
|
process.env["INPUT_ENTERPRISE-SLUG"] = "test-enterprise";
|
||||||
|
|
||||||
|
|
||||||
|
// Mock the /app/installations endpoint to return only non-enterprise installations
|
||||||
|
mockPool
|
||||||
|
.intercept({
|
||||||
|
path: "/app/installations",
|
||||||
|
method: "GET",
|
||||||
|
headers: {
|
||||||
|
accept: "application/vnd.github.v3+json",
|
||||||
|
"user-agent": "actions/create-github-app-token",
|
||||||
|
// Intentionally omitting the `authorization` header, since JWT creation is not idempotent.
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.reply(
|
||||||
|
200,
|
||||||
|
[
|
||||||
|
{
|
||||||
|
id: "111111",
|
||||||
|
app_slug: "github-actions",
|
||||||
|
target_type: "Organization",
|
||||||
|
account: { login: "some-org" }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "222222",
|
||||||
|
app_slug: "github-actions",
|
||||||
|
target_type: "User",
|
||||||
|
account: { login: "some-user" }
|
||||||
|
}
|
||||||
|
],
|
||||||
|
{ headers: { "content-type": "application/json" } }
|
||||||
|
);
|
||||||
|
});
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
import { DEFAULT_ENV } from "./main.js";
|
||||||
|
|
||||||
|
// Verify `main` exits with an error when `enterprise-slug` is used with both `owner` and `repositories` inputs.
|
||||||
|
try {
|
||||||
|
// Set up environment with enterprise-slug, owner, and repositories all set
|
||||||
|
for (const [key, value] of Object.entries(DEFAULT_ENV)) {
|
||||||
|
process.env[key] = value;
|
||||||
|
}
|
||||||
|
process.env["INPUT_ENTERPRISE-SLUG"] = "test-enterprise";
|
||||||
|
process.env.INPUT_OWNER = "test-owner";
|
||||||
|
process.env.INPUT_REPOSITORIES = "repo1,repo2";
|
||||||
|
|
||||||
|
await import("../main.js");
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error.message);
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
import { DEFAULT_ENV } from "./main.js";
|
||||||
|
|
||||||
|
// Verify `main` exits with an error when `enterprise-slug` is used with `owner` input.
|
||||||
|
try {
|
||||||
|
// Set up environment with enterprise-slug and owner set
|
||||||
|
for (const [key, value] of Object.entries(DEFAULT_ENV)) {
|
||||||
|
process.env[key] = value;
|
||||||
|
}
|
||||||
|
process.env["INPUT_ENTERPRISE-SLUG"] = "test-enterprise";
|
||||||
|
process.env.INPUT_OWNER = "test-owner";
|
||||||
|
|
||||||
|
await import("../main.js");
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error.message);
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
import { DEFAULT_ENV } from "./main.js";
|
||||||
|
|
||||||
|
// Verify `main` exits with an error when `enterprise-slug` is used with `repositories` input.
|
||||||
|
try {
|
||||||
|
// Set up environment with enterprise-slug and repositories set
|
||||||
|
for (const [key, value] of Object.entries(DEFAULT_ENV)) {
|
||||||
|
process.env[key] = value;
|
||||||
|
}
|
||||||
|
process.env["INPUT_ENTERPRISE-SLUG"] = "test-enterprise";
|
||||||
|
process.env.INPUT_REPOSITORIES = "repo1,repo2";
|
||||||
|
|
||||||
|
await import("../main.js");
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error.message);
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
import { test } from "./main.js";
|
||||||
|
|
||||||
|
// Verify `main` successfully obtains a token when only the `enterprise-slug` input is set.
|
||||||
|
await test((mockPool) => {
|
||||||
|
process.env["INPUT_ENTERPRISE-SLUG"] = "test-enterprise";
|
||||||
|
delete process.env.INPUT_OWNER;
|
||||||
|
delete process.env.INPUT_REPOSITORIES;
|
||||||
|
|
||||||
|
// Mock the /app/installations endpoint to return an enterprise installation
|
||||||
|
const mockInstallationId = "123456";
|
||||||
|
const mockAppSlug = "github-actions";
|
||||||
|
mockPool
|
||||||
|
.intercept({
|
||||||
|
path: "/app/installations",
|
||||||
|
method: "GET",
|
||||||
|
headers: {
|
||||||
|
accept: "application/vnd.github.v3+json",
|
||||||
|
"user-agent": "actions/create-github-app-token",
|
||||||
|
// Intentionally omitting the `authorization` header, since JWT creation is not idempotent.
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.reply(
|
||||||
|
200,
|
||||||
|
[
|
||||||
|
{
|
||||||
|
id: mockInstallationId,
|
||||||
|
app_slug: mockAppSlug,
|
||||||
|
target_type: "Enterprise",
|
||||||
|
account: { login: "test-enterprise", slug: "test-enterprise" }
|
||||||
|
}
|
||||||
|
],
|
||||||
|
{ headers: { "content-type": "application/json" } }
|
||||||
|
);
|
||||||
|
});
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
import { test } from "./main.js";
|
||||||
|
|
||||||
|
// Verify `main` successfully generates enterprise token with basic functionality.
|
||||||
|
await test((mockPool) => {
|
||||||
|
process.env["INPUT_ENTERPRISE-SLUG"] = "test-enterprise";
|
||||||
|
delete process.env.INPUT_OWNER;
|
||||||
|
delete process.env.INPUT_REPOSITORIES;
|
||||||
|
|
||||||
|
// Mock the /app/installations endpoint to return an enterprise installation
|
||||||
|
const mockInstallationId = "123456";
|
||||||
|
const mockAppSlug = "github-actions";
|
||||||
|
mockPool
|
||||||
|
.intercept({
|
||||||
|
path: "/app/installations",
|
||||||
|
method: "GET",
|
||||||
|
headers: {
|
||||||
|
accept: "application/vnd.github.v3+json",
|
||||||
|
"user-agent": "actions/create-github-app-token",
|
||||||
|
// Intentionally omitting the `authorization` header, since JWT creation is not idempotent.
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.reply(
|
||||||
|
200,
|
||||||
|
[
|
||||||
|
{
|
||||||
|
id: mockInstallationId,
|
||||||
|
app_slug: mockAppSlug,
|
||||||
|
target_type: "Enterprise",
|
||||||
|
account: { login: "test-enterprise", slug: "test-enterprise" }
|
||||||
|
}
|
||||||
|
],
|
||||||
|
{ headers: { "content-type": "application/json" } }
|
||||||
|
);
|
||||||
|
});
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
import { test } from "./main.js";
|
||||||
|
|
||||||
|
// Verify `main` successfully generates enterprise token with specific permissions.
|
||||||
|
await test((mockPool) => {
|
||||||
|
process.env["INPUT_ENTERPRISE-SLUG"] = "test-enterprise";
|
||||||
|
delete process.env.INPUT_OWNER;
|
||||||
|
delete process.env.INPUT_REPOSITORIES;
|
||||||
|
process.env["INPUT_PERMISSION-ENTERPRISE-ORGANIZATIONS"] = "read";
|
||||||
|
process.env["INPUT_PERMISSION-ENTERPRISE-PEOPLE"] = "write";
|
||||||
|
|
||||||
|
// Mock the /app/installations endpoint to return an enterprise installation
|
||||||
|
const mockInstallationId = "123456";
|
||||||
|
const mockAppSlug = "github-actions";
|
||||||
|
mockPool
|
||||||
|
.intercept({
|
||||||
|
path: "/app/installations",
|
||||||
|
method: "GET",
|
||||||
|
headers: {
|
||||||
|
accept: "application/vnd.github.v3+json",
|
||||||
|
"user-agent": "actions/create-github-app-token",
|
||||||
|
// Intentionally omitting the `authorization` header, since JWT creation is not idempotent.
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.reply(
|
||||||
|
200,
|
||||||
|
[
|
||||||
|
{
|
||||||
|
id: mockInstallationId,
|
||||||
|
app_slug: mockAppSlug,
|
||||||
|
target_type: "Enterprise",
|
||||||
|
account: { login: "test-enterprise", slug: "test-enterprise" }
|
||||||
|
}
|
||||||
|
],
|
||||||
|
{ headers: { "content-type": "application/json" } }
|
||||||
|
);
|
||||||
|
});
|
||||||
@@ -39,6 +39,139 @@ Generated by [AVA](https://avajs.dev).
|
|||||||
POST /api/v3/app/installations/123456/access_tokens␊
|
POST /api/v3/app/installations/123456/access_tokens␊
|
||||||
{"repositories":["create-github-app-token"]}`
|
{"repositories":["create-github-app-token"]}`
|
||||||
|
|
||||||
|
## main-enterprise-installation-not-found.test.js
|
||||||
|
|
||||||
|
> stderr
|
||||||
|
|
||||||
|
`Error: No enterprise installation found matching the name test-enterprise. Available installations: Organization:some-org, User:some-user␊
|
||||||
|
at getTokenFromEnterprise (file:///Users/parkerbxyz/.copilot/worktrees/create-github-app-token/pr-263/lib/main.js:230:11)␊
|
||||||
|
at process.processTicksAndRejections (node:internal/process/task_queues:104:5)␊
|
||||||
|
at async pRetry (file:///Users/parkerbxyz/.copilot/worktrees/create-github-app-token/pr-263/node_modules/p-retry/index.js:197:19)␊
|
||||||
|
at async main (file:///Users/parkerbxyz/.copilot/worktrees/create-github-app-token/pr-263/lib/main.js:95:52)␊
|
||||||
|
at async test (file:///Users/parkerbxyz/.copilot/worktrees/create-github-app-token/pr-263/tests/main.js:111:3)␊
|
||||||
|
at async file:///Users/parkerbxyz/.copilot/worktrees/create-github-app-token/pr-263/tests/main-enterprise-installation-not-found.test.js:5:1`
|
||||||
|
|
||||||
|
> stdout
|
||||||
|
|
||||||
|
`Creating enterprise installation token for enterprise "test-enterprise".␊
|
||||||
|
Failed to create token for enterprise "test-enterprise" (attempt 1): undefined␊
|
||||||
|
--- REQUESTS ---␊
|
||||||
|
GET /app/installations`
|
||||||
|
|
||||||
|
## main-enterprise-mutual-exclusivity-both.test.js
|
||||||
|
|
||||||
|
> stderr
|
||||||
|
|
||||||
|
`Error: Cannot use 'enterprise-slug' input with 'owner' or 'repositories' inputs␊
|
||||||
|
at main (file:///Users/parkerbxyz/.copilot/worktrees/create-github-app-token/pr-263/lib/main.js:31:11)␊
|
||||||
|
at file:///Users/parkerbxyz/.copilot/worktrees/create-github-app-token/pr-263/main.js:33:16␊
|
||||||
|
at ModuleJob.run (node:internal/modules/esm/module_job:430:25)␊
|
||||||
|
at async onImport.tracePromise.__proto__ (node:internal/modules/esm/loader:639:26)␊
|
||||||
|
at async file:///Users/parkerbxyz/.copilot/worktrees/create-github-app-token/pr-263/tests/main-enterprise-mutual-exclusivity-both.test.js:13:3`
|
||||||
|
|
||||||
|
> stdout
|
||||||
|
|
||||||
|
''
|
||||||
|
|
||||||
|
## main-enterprise-mutual-exclusivity-owner.test.js
|
||||||
|
|
||||||
|
> stderr
|
||||||
|
|
||||||
|
`Error: Cannot use 'enterprise-slug' input with 'owner' or 'repositories' inputs␊
|
||||||
|
at main (file:///Users/parkerbxyz/.copilot/worktrees/create-github-app-token/pr-263/lib/main.js:31:11)␊
|
||||||
|
at file:///Users/parkerbxyz/.copilot/worktrees/create-github-app-token/pr-263/main.js:33:16␊
|
||||||
|
at ModuleJob.run (node:internal/modules/esm/module_job:430:25)␊
|
||||||
|
at async onImport.tracePromise.__proto__ (node:internal/modules/esm/loader:639:26)␊
|
||||||
|
at async file:///Users/parkerbxyz/.copilot/worktrees/create-github-app-token/pr-263/tests/main-enterprise-mutual-exclusivity-owner.test.js:12:3`
|
||||||
|
|
||||||
|
> stdout
|
||||||
|
|
||||||
|
''
|
||||||
|
|
||||||
|
## main-enterprise-mutual-exclusivity-repositories.test.js
|
||||||
|
|
||||||
|
> stderr
|
||||||
|
|
||||||
|
`Error: Cannot use 'enterprise-slug' input with 'owner' or 'repositories' inputs␊
|
||||||
|
at main (file:///Users/parkerbxyz/.copilot/worktrees/create-github-app-token/pr-263/lib/main.js:31:11)␊
|
||||||
|
at file:///Users/parkerbxyz/.copilot/worktrees/create-github-app-token/pr-263/main.js:33:16␊
|
||||||
|
at ModuleJob.run (node:internal/modules/esm/module_job:430:25)␊
|
||||||
|
at async onImport.tracePromise.__proto__ (node:internal/modules/esm/loader:639:26)␊
|
||||||
|
at async file:///Users/parkerbxyz/.copilot/worktrees/create-github-app-token/pr-263/tests/main-enterprise-mutual-exclusivity-repositories.test.js:12:3`
|
||||||
|
|
||||||
|
> stdout
|
||||||
|
|
||||||
|
''
|
||||||
|
|
||||||
|
## main-enterprise-only-success.test.js
|
||||||
|
|
||||||
|
> stderr
|
||||||
|
|
||||||
|
''
|
||||||
|
|
||||||
|
> stdout
|
||||||
|
|
||||||
|
`Creating enterprise installation token for enterprise "test-enterprise".␊
|
||||||
|
::add-mask::ghs_16C7e42F292c6912E7710c838347Ae178B4a␊
|
||||||
|
␊
|
||||||
|
::set-output name=token::ghs_16C7e42F292c6912E7710c838347Ae178B4a␊
|
||||||
|
␊
|
||||||
|
::set-output name=installation-id::123456␊
|
||||||
|
␊
|
||||||
|
::set-output name=app-slug::github-actions␊
|
||||||
|
::save-state name=token::ghs_16C7e42F292c6912E7710c838347Ae178B4a␊
|
||||||
|
::save-state name=expiresAt::2016-07-11T22:14:10Z␊
|
||||||
|
--- REQUESTS ---␊
|
||||||
|
GET /app/installations␊
|
||||||
|
POST /app/installations/123456/access_tokens␊
|
||||||
|
null`
|
||||||
|
|
||||||
|
## main-enterprise-token-success.test.js
|
||||||
|
|
||||||
|
> stderr
|
||||||
|
|
||||||
|
''
|
||||||
|
|
||||||
|
> stdout
|
||||||
|
|
||||||
|
`Creating enterprise installation token for enterprise "test-enterprise".␊
|
||||||
|
::add-mask::ghs_16C7e42F292c6912E7710c838347Ae178B4a␊
|
||||||
|
␊
|
||||||
|
::set-output name=token::ghs_16C7e42F292c6912E7710c838347Ae178B4a␊
|
||||||
|
␊
|
||||||
|
::set-output name=installation-id::123456␊
|
||||||
|
␊
|
||||||
|
::set-output name=app-slug::github-actions␊
|
||||||
|
::save-state name=token::ghs_16C7e42F292c6912E7710c838347Ae178B4a␊
|
||||||
|
::save-state name=expiresAt::2016-07-11T22:14:10Z␊
|
||||||
|
--- REQUESTS ---␊
|
||||||
|
GET /app/installations␊
|
||||||
|
POST /app/installations/123456/access_tokens␊
|
||||||
|
null`
|
||||||
|
|
||||||
|
## main-enterprise-token-with-permissions.test.js
|
||||||
|
|
||||||
|
> stderr
|
||||||
|
|
||||||
|
''
|
||||||
|
|
||||||
|
> stdout
|
||||||
|
|
||||||
|
`Creating enterprise installation token for enterprise "test-enterprise".␊
|
||||||
|
::add-mask::ghs_16C7e42F292c6912E7710c838347Ae178B4a␊
|
||||||
|
␊
|
||||||
|
::set-output name=token::ghs_16C7e42F292c6912E7710c838347Ae178B4a␊
|
||||||
|
␊
|
||||||
|
::set-output name=installation-id::123456␊
|
||||||
|
␊
|
||||||
|
::set-output name=app-slug::github-actions␊
|
||||||
|
::save-state name=token::ghs_16C7e42F292c6912E7710c838347Ae178B4a␊
|
||||||
|
::save-state name=expiresAt::2016-07-11T22:14:10Z␊
|
||||||
|
--- REQUESTS ---␊
|
||||||
|
GET /app/installations␊
|
||||||
|
POST /app/installations/123456/access_tokens␊
|
||||||
|
{"permissions":{"enterprise_organizations":"read","enterprise_people":"write"}}`
|
||||||
|
|
||||||
## main-missing-owner.test.js
|
## main-missing-owner.test.js
|
||||||
|
|
||||||
> stderr
|
> stderr
|
||||||
|
|||||||
Binary file not shown.
Reference in New Issue
Block a user