feat: github-api-url (#88)
closes #77 --------- Co-authored-by: Parker Brown <17183625+parkerbxyz@users.noreply.github.com>
This commit is contained in:
@@ -15,7 +15,11 @@ In order to use this action, you need to:
|
||||
### Create a token for the current repository
|
||||
|
||||
```yaml
|
||||
on: [issues]
|
||||
name: Run tests on staging
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
|
||||
jobs:
|
||||
hello-world:
|
||||
@@ -26,11 +30,10 @@ jobs:
|
||||
with:
|
||||
app-id: ${{ vars.APP_ID }}
|
||||
private-key: ${{ secrets.PRIVATE_KEY }}
|
||||
- uses: peter-evans/create-or-update-comment@v3
|
||||
github-api-url: "https://github.acme-inc.com/api/v3"
|
||||
- uses: ./actions/staging-tests
|
||||
with:
|
||||
token: ${{ steps.app-token.outputs.token }}
|
||||
issue-number: ${{ github.event.issue.number }}
|
||||
body: "Hello, World!"
|
||||
```
|
||||
|
||||
### Use app token with `actions/checkout`
|
||||
@@ -146,7 +149,7 @@ jobs:
|
||||
run: echo 'matrix=[{"owner":"owner1"},{"owner":"owner2","repos":["repo1"]}]' >>"$GITHUB_OUTPUT"
|
||||
|
||||
use-matrix:
|
||||
name: '@${{ matrix.owners-and-repos.owner }} installation'
|
||||
name: "@${{ matrix.owners-and-repos.owner }} installation"
|
||||
needs: [set-matrix]
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
@@ -172,6 +175,12 @@ jobs:
|
||||
MULTILINE_JSON_STRING: ${{ steps.get-installation-repositories.outputs.data }}
|
||||
```
|
||||
|
||||
### Run the workflow in a github.com repository against an organization in GitHub Enterprise Server
|
||||
|
||||
```yaml
|
||||
on: [push]
|
||||
```
|
||||
|
||||
## Inputs
|
||||
|
||||
### `app-id`
|
||||
@@ -197,6 +206,10 @@ jobs:
|
||||
|
||||
**Optional:** If truthy, the token will not be revoked when the current job is complete.
|
||||
|
||||
### `github-api-url`
|
||||
|
||||
**Optional:** The URL of the GitHub REST API. Defaults to the URL of the GitHub Rest API where the workflow is run from.
|
||||
|
||||
## Outputs
|
||||
|
||||
### `token`
|
||||
|
||||
@@ -32,6 +32,11 @@ inputs:
|
||||
description: "If truthy, the token will not be revoked when the current job is complete"
|
||||
required: false
|
||||
deprecationMessage: "'skip_token_revoke' is deprecated and will be removed in a future version. Use 'skip-token-revoke' instead."
|
||||
# Make GitHub API configurable to support non-GitHub Cloud use cases
|
||||
# see https://github.com/actions/create-github-app-token/issues/77
|
||||
github-api-url:
|
||||
description: The URL of the GitHub REST API.
|
||||
default: ${{ github.api_url }}
|
||||
outputs:
|
||||
token:
|
||||
description: "GitHub installation access token"
|
||||
|
||||
Vendored
+6
-8
@@ -3023,7 +3023,7 @@ var require_dist_node6 = __commonJS({
|
||||
module2.exports = __toCommonJS2(dist_src_exports);
|
||||
function oauthAuthorizationUrl(options) {
|
||||
const clientType = options.clientType || "oauth-app";
|
||||
const baseUrl = options.baseUrl || "https://github.com";
|
||||
const baseUrl2 = options.baseUrl || "https://github.com";
|
||||
const result = {
|
||||
clientType,
|
||||
allowSignup: options.allowSignup === false ? false : true,
|
||||
@@ -3037,7 +3037,7 @@ var require_dist_node6 = __commonJS({
|
||||
const scopes = "scopes" in options ? options.scopes : [];
|
||||
result.scopes = typeof scopes === "string" ? scopes.split(/[,\s]+/).filter(Boolean) : scopes;
|
||||
}
|
||||
result.url = urlBuilderAuthorize(`${baseUrl}/login/oauth/authorize`, result);
|
||||
result.url = urlBuilderAuthorize(`${baseUrl2}/login/oauth/authorize`, result);
|
||||
return result;
|
||||
}
|
||||
function urlBuilderAuthorize(base, options) {
|
||||
@@ -3149,10 +3149,10 @@ var require_dist_node7 = __commonJS({
|
||||
request: request2 = import_request3.request,
|
||||
...options
|
||||
}) {
|
||||
const baseUrl = requestToOAuthBaseUrl(request2);
|
||||
const baseUrl2 = requestToOAuthBaseUrl(request2);
|
||||
return (0, import_oauth_authorization_url.oauthAuthorizationUrl)({
|
||||
...options,
|
||||
baseUrl
|
||||
baseUrl: baseUrl2
|
||||
});
|
||||
}
|
||||
var import_request22 = require_dist_node5();
|
||||
@@ -10464,7 +10464,6 @@ async function getTokenFromRepository(request2, auth, parsedOwner, parsedReposit
|
||||
// lib/request.js
|
||||
var import_request = __toESM(require_dist_node5(), 1);
|
||||
var request_default = import_request.request.defaults({
|
||||
baseUrl: process.env["GITHUB_API_URL"],
|
||||
headers: {
|
||||
"user-agent": "actions/create-github-app-token"
|
||||
}
|
||||
@@ -10490,6 +10489,7 @@ var repositories = import_core.default.getInput("repositories");
|
||||
var skipTokenRevoke = Boolean(
|
||||
import_core.default.getInput("skip-token-revoke") || import_core.default.getInput("skip_token_revoke")
|
||||
);
|
||||
var baseUrl = import_core.default.getInput("github-api-url").replace(/\/$/, "");
|
||||
main(
|
||||
appId,
|
||||
privateKey,
|
||||
@@ -10497,9 +10497,7 @@ main(
|
||||
repositories,
|
||||
import_core.default,
|
||||
import_auth_app.createAppAuth,
|
||||
request_default.defaults({
|
||||
baseUrl: process.env["GITHUB_API_URL"]
|
||||
}),
|
||||
request_default.defaults({ baseUrl }),
|
||||
skipTokenRevoke
|
||||
).catch((error) => {
|
||||
console.error(error);
|
||||
|
||||
Vendored
+2
-7
@@ -3030,19 +3030,14 @@ function tokenExpiresIn(expiresAt) {
|
||||
// lib/request.js
|
||||
var import_request = __toESM(require_dist_node5(), 1);
|
||||
var request_default = import_request.request.defaults({
|
||||
baseUrl: process.env["GITHUB_API_URL"],
|
||||
headers: {
|
||||
"user-agent": "actions/create-github-app-token"
|
||||
}
|
||||
});
|
||||
|
||||
// post.js
|
||||
post(
|
||||
import_core.default,
|
||||
request_default.defaults({
|
||||
baseUrl: process.env["GITHUB_API_URL"]
|
||||
})
|
||||
).catch((error) => {
|
||||
var baseUrl = import_core.default.getInput("github-api-url").replace(/\/$/, "");
|
||||
post(import_core.default, request_default.defaults({ baseUrl })).catch((error) => {
|
||||
console.error(error);
|
||||
import_core.default.setFailed(error.message);
|
||||
});
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { request } from "@octokit/request";
|
||||
|
||||
export default request.defaults({
|
||||
baseUrl: process.env["GITHUB_API_URL"],
|
||||
headers: {
|
||||
"user-agent": "actions/create-github-app-token",
|
||||
},
|
||||
|
||||
@@ -31,6 +31,8 @@ const skipTokenRevoke = Boolean(
|
||||
core.getInput("skip-token-revoke") || core.getInput("skip_token_revoke")
|
||||
);
|
||||
|
||||
const baseUrl = core.getInput("github-api-url").replace(/\/$/, "");
|
||||
|
||||
main(
|
||||
appId,
|
||||
privateKey,
|
||||
@@ -38,9 +40,7 @@ main(
|
||||
repositories,
|
||||
core,
|
||||
createAppAuth,
|
||||
request.defaults({
|
||||
baseUrl: process.env["GITHUB_API_URL"],
|
||||
}),
|
||||
request.defaults({ baseUrl }),
|
||||
skipTokenRevoke
|
||||
).catch((error) => {
|
||||
/* c8 ignore next 3 */
|
||||
|
||||
@@ -5,12 +5,9 @@ import core from "@actions/core";
|
||||
import { post } from "./lib/post.js";
|
||||
import request from "./lib/request.js";
|
||||
|
||||
post(
|
||||
core,
|
||||
request.defaults({
|
||||
baseUrl: process.env["GITHUB_API_URL"],
|
||||
})
|
||||
).catch((error) => {
|
||||
const baseUrl = core.getInput("github-api-url").replace(/\/$/, "");
|
||||
|
||||
post(core, request.defaults({ baseUrl })).catch((error) => {
|
||||
/* c8 ignore next 3 */
|
||||
console.error(error);
|
||||
core.setFailed(error.message);
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
import { test, DEFAULT_ENV } from "./main.js";
|
||||
|
||||
// Verify that main works with a custom GitHub API URL passed as `github-api-url` input
|
||||
await test(
|
||||
() => {
|
||||
process.env.INPUT_OWNER = process.env.GITHUB_REPOSITORY_OWNER;
|
||||
process.env.INPUT_REPOSITORIES = process.env.GITHUB_REPOSITORY;
|
||||
},
|
||||
{
|
||||
...DEFAULT_ENV,
|
||||
"INPUT_GITHUB-API-URL": "https://github.acme-inc.com/api/v3",
|
||||
}
|
||||
);
|
||||
@@ -2,10 +2,10 @@ import { test } from "./main.js";
|
||||
|
||||
// Verify `main` retry when the GitHub API returns a 500 error.
|
||||
await test((mockPool) => {
|
||||
process.env.INPUT_OWNER = 'actions'
|
||||
process.env.INPUT_REPOSITORIES = 'failed-repo';
|
||||
const owner = process.env.INPUT_OWNER
|
||||
const repo = process.env.INPUT_REPOSITORIES
|
||||
process.env.INPUT_OWNER = "actions";
|
||||
process.env.INPUT_REPOSITORIES = "failed-repo";
|
||||
const owner = process.env.INPUT_OWNER;
|
||||
const repo = process.env.INPUT_REPOSITORIES;
|
||||
const mockInstallationId = "123456";
|
||||
|
||||
mockPool
|
||||
@@ -18,9 +18,9 @@ await test((mockPool) => {
|
||||
// Intentionally omitting the `authorization` header, since JWT creation is not idempotent.
|
||||
},
|
||||
})
|
||||
.reply(500, 'GitHub API not available')
|
||||
|
||||
mockPool
|
||||
.reply(500, "GitHub API not available");
|
||||
|
||||
mockPool
|
||||
.intercept({
|
||||
path: `/repos/${owner}/${repo}/installation`,
|
||||
method: "GET",
|
||||
@@ -35,5 +35,4 @@ await test((mockPool) => {
|
||||
{ id: mockInstallationId },
|
||||
{ headers: { "content-type": "application/json" } }
|
||||
);
|
||||
|
||||
});
|
||||
|
||||
@@ -17,8 +17,8 @@ await test((mockPool) => {
|
||||
// Intentionally omitting the `authorization` header, since JWT creation is not idempotent.
|
||||
},
|
||||
})
|
||||
.reply(500, 'GitHub API not available')
|
||||
mockPool
|
||||
.reply(500, "GitHub API not available");
|
||||
mockPool
|
||||
.intercept({
|
||||
path: `/orgs/${process.env.INPUT_OWNER}/installation`,
|
||||
method: "GET",
|
||||
|
||||
+21
-14
@@ -2,14 +2,15 @@
|
||||
// @ts-check
|
||||
import { MockAgent, setGlobalDispatcher } from "undici";
|
||||
|
||||
export async function test(cb = (_mockPool) => {}) {
|
||||
// Set required environment variables and inputs
|
||||
process.env.GITHUB_REPOSITORY_OWNER = "actions";
|
||||
process.env.GITHUB_REPOSITORY = "actions/create-github-app-token";
|
||||
export const DEFAULT_ENV = {
|
||||
GITHUB_REPOSITORY_OWNER: "actions",
|
||||
GITHUB_REPOSITORY: "actions/create-github-app-token",
|
||||
// inputs are set as environment variables with the prefix INPUT_
|
||||
// https://docs.github.com/en/actions/creating-actions/metadata-syntax-for-github-actions#example-specifying-inputs
|
||||
process.env["INPUT_APP-ID"] = "123456";
|
||||
process.env["INPUT_PRIVATE-KEY"] = `-----BEGIN RSA PRIVATE KEY-----
|
||||
"INPUT_GITHUB-API-URL": "https://api.github.com",
|
||||
"INPUT_APP-ID": "123456",
|
||||
// This key is invalidated. It’s from https://github.com/octokit/auth-app.js/issues/465#issuecomment-1564998327.
|
||||
"INPUT_PRIVATE-KEY": `-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEowIBAAKCAQEA280nfuUM9w00Ib9E2rvZJ6Qu3Ua3IqR34ZlK53vn/Iobn2EL
|
||||
Z9puc5Q/nFBU15NKwHyQNb+OG2hTCkjd1Xi9XPzEOH1r42YQmTGq8YCkUSkk6KZA
|
||||
5dnhLwN9pFquT9fQgrf4r1D5GJj3rqvj8JDr1sBmunArqY5u4gziSrIohcjLIZV0
|
||||
@@ -35,27 +36,33 @@ r4J2gqb0xTDfq7gLMNrIXc2QQM4gKbnJp60JQM3p9NmH8huavBZGvSvNzTwXyGG3
|
||||
so0tiQKBgGQXZaxaXhYUcxYHuCkQ3V4Vsj3ezlM92xXlP32SGFm3KgFhYy9kATxw
|
||||
Cax1ytZzvlrKLQyQFVK1COs2rHt7W4cJ7op7C8zXfsigXCiejnS664oAuX8sQZID
|
||||
x3WQZRiXlWejSMUAHuMwXrhGlltF3lw83+xAjnqsVp75kGS6OH61
|
||||
-----END RSA PRIVATE KEY-----`; // This key is invalidated. It’s from https://github.com/octokit/auth-app.js/issues/465#issuecomment-1564998327.
|
||||
-----END RSA PRIVATE KEY-----`,
|
||||
};
|
||||
|
||||
export async function test(cb = (_mockPool) => {}, env = DEFAULT_ENV) {
|
||||
for (const [key, value] of Object.entries(env)) {
|
||||
process.env[key] = value;
|
||||
}
|
||||
|
||||
// Set up mocking
|
||||
const baseUrl = new URL(env["INPUT_GITHUB-API-URL"]);
|
||||
const basePath = baseUrl.pathname === '/' ? '' : baseUrl.pathname;
|
||||
const mockAgent = new MockAgent();
|
||||
mockAgent.disableNetConnect();
|
||||
setGlobalDispatcher(mockAgent);
|
||||
const mockPool = mockAgent.get("https://api.github.com");
|
||||
const mockPool = mockAgent.get(baseUrl.origin);
|
||||
|
||||
// Calling `auth({ type: "app" })` to obtain a JWT doesn’t make network requests, so no need to intercept.
|
||||
|
||||
// Mock installation id request
|
||||
const mockInstallationId = "123456";
|
||||
const owner = process.env.INPUT_OWNER ?? process.env.GITHUB_REPOSITORY_OWNER;
|
||||
const owner = env.INPUT_OWNER ?? env.GITHUB_REPOSITORY_OWNER;
|
||||
const repo = encodeURIComponent(
|
||||
(process.env.INPUT_REPOSITORIES ?? process.env.GITHUB_REPOSITORY).split(
|
||||
","
|
||||
)[0]
|
||||
(env.INPUT_REPOSITORIES ?? env.GITHUB_REPOSITORY).split(",")[0]
|
||||
);
|
||||
mockPool
|
||||
.intercept({
|
||||
path: `/repos/${owner}/${repo}/installation`,
|
||||
path: `${basePath}/repos/${owner}/${repo}/installation`,
|
||||
method: "GET",
|
||||
headers: {
|
||||
accept: "application/vnd.github.v3+json",
|
||||
@@ -75,7 +82,7 @@ x3WQZRiXlWejSMUAHuMwXrhGlltF3lw83+xAjnqsVp75kGS6OH61
|
||||
const mockExpiresAt = "2016-07-11T22:14:10Z";
|
||||
mockPool
|
||||
.intercept({
|
||||
path: `/app/installations/${mockInstallationId}/access_tokens`,
|
||||
path: `${basePath}/app/installations/${mockInstallationId}/access_tokens`,
|
||||
method: "POST",
|
||||
headers: {
|
||||
accept: "application/vnd.github.v3+json",
|
||||
|
||||
@@ -4,8 +4,14 @@ import { MockAgent, setGlobalDispatcher } from "undici";
|
||||
// https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions#sending-values-to-the-pre-and-post-actions
|
||||
process.env.STATE_token = "secret123";
|
||||
|
||||
// inputs are set as environment variables with the prefix INPUT_
|
||||
// https://docs.github.com/en/actions/creating-actions/metadata-syntax-for-github-actions#example-specifying-inputs
|
||||
process.env["INPUT_GITHUB-API-URL"] = "https://api.github.com";
|
||||
|
||||
// 1 hour in the future, not expired
|
||||
process.env.STATE_expiresAt = new Date(Date.now() + 1000 * 60 * 60).toISOString();
|
||||
process.env.STATE_expiresAt = new Date(
|
||||
Date.now() + 1000 * 60 * 60
|
||||
).toISOString();
|
||||
|
||||
const mockAgent = new MockAgent();
|
||||
|
||||
|
||||
@@ -4,6 +4,10 @@ import { MockAgent, setGlobalDispatcher } from "undici";
|
||||
// https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions#sending-values-to-the-pre-and-post-actions
|
||||
process.env.STATE_token = "secret123";
|
||||
|
||||
// inputs are set as environment variables with the prefix INPUT_
|
||||
// https://docs.github.com/en/actions/creating-actions/metadata-syntax-for-github-actions#example-specifying-inputs
|
||||
process.env["INPUT_GITHUB-API-URL"] = "https://api.github.com";
|
||||
|
||||
// 1 hour in the future, not expired
|
||||
process.env.STATE_expiresAt = new Date(Date.now() + 1000 * 60 * 60).toISOString();
|
||||
|
||||
|
||||
@@ -16,6 +16,22 @@ Generated by [AVA](https://avajs.dev).
|
||||
private_key — 'private_key' is deprecated and will be removed in a future version. Use 'private-key' instead.␊
|
||||
skip_token_revoke — 'skip_token_revoke' is deprecated and will be removed in a future version. Use 'skip-token-revoke' instead.`
|
||||
|
||||
## main-custom-github-api-url.test.js
|
||||
|
||||
> stderr
|
||||
|
||||
''
|
||||
|
||||
> stdout
|
||||
|
||||
`owner and repositories set, creating token for repositories "actions/create-github-app-token" owned by "actions"␊
|
||||
::add-mask::ghs_16C7e42F292c6912E7710c838347Ae178B4a␊
|
||||
␊
|
||||
::set-output name=token::ghs_16C7e42F292c6912E7710c838347Ae178B4a␊
|
||||
::save-state name=token::ghs_16C7e42F292c6912E7710c838347Ae178B4a␊
|
||||
␊
|
||||
::set-output name=expiresAt::2016-07-11T22:14:10Z`
|
||||
|
||||
## main-missing-app-id.test.js
|
||||
|
||||
> stderr
|
||||
|
||||
Binary file not shown.
Reference in New Issue
Block a user