diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 8774c1b..2040e99 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -4,6 +4,7 @@ on: push: branches: - main + - beta pull_request: merge_group: workflow_dispatch: @@ -33,7 +34,7 @@ jobs: name: end-to-end runs-on: ubuntu-latest # do not run from forks, as forks don’t have access to repository secrets - if: github.event_name == 'merge_group' || github.event.pull_request.head.repo.owner.login == github.event.pull_request.base.repo.owner.login + if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.owner.login == github.event.pull_request.base.repo.owner.login steps: - uses: actions/checkout@v6 - uses: actions/setup-node@v6 @@ -53,3 +54,28 @@ jobs: with: route: GET /installation/repositories - run: echo '${{ steps.get-repository.outputs.data }}' + + end-to-end-proxy: + name: End-to-End with unreachable proxy + runs-on: ubuntu-latest + # do not run from forks, as forks don’t have access to repository secrets + if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.owner.login == github.event.pull_request.base.repo.owner.login + steps: + - uses: actions/checkout@v5 + - uses: actions/setup-node@v4 + with: + node-version-file: package.json + cache: 'npm' + - run: npm ci + - run: npm run build + - uses: ./ # Uses the action in the root directory + continue-on-error: true + id: test + env: + NODE_USE_ENV_PROXY: "1" + https_proxy: http://127.0.0.1:9 + with: + app-id: ${{ vars.TEST_APP_ID }} + private-key: ${{ secrets.TEST_APP_PRIVATE_KEY }} + - name: Assert action failed through unreachable proxy + run: test "${{ steps.test.outcome }}" = "failure" diff --git a/README.md b/README.md index 7e13c8d..03659c2 100644 --- a/README.md +++ b/README.md @@ -296,6 +296,24 @@ jobs: GITHUB_TOKEN: ${{ steps.create_token.outputs.token }} ``` +### Proxy support + +This action relies on Node.js native proxy support. + +If you set `HTTP_PROXY` or `HTTPS_PROXY`, also set `NODE_USE_ENV_PROXY: "1"` on the action step so Node.js honors those variables. If you need proxy bypass rules, set `NO_PROXY` alongside them. + +```yaml +- uses: actions/create-github-app-token@v3 + id: app-token + env: + HTTPS_PROXY: http://proxy.example.com:8080 + NO_PROXY: github.example.com + NODE_USE_ENV_PROXY: "1" + with: + app-id: ${{ vars.APP_ID }} + private-key: ${{ secrets.PRIVATE_KEY }} +``` + ## Inputs ### `app-id` diff --git a/lib/request.js b/lib/request.js index f35d330..5bf924a 100644 --- a/lib/request.js +++ b/lib/request.js @@ -4,6 +4,31 @@ import { request } from "@octokit/request"; // Get the GitHub API URL from the action input and remove any trailing slash const baseUrl = core.getInput("github-api-url").replace(/\/$/, ""); +const proxyEnvironmentKeys = [ + "https_proxy", + "HTTPS_PROXY", + "http_proxy", + "HTTP_PROXY", +]; + +function proxyEnvironmentConfigured() { + return proxyEnvironmentKeys.some((key) => process.env[key]); +} + +function nativeProxySupportEnabled() { + return process.env.NODE_USE_ENV_PROXY === "1"; +} + +export function ensureNativeProxySupport() { + if (!proxyEnvironmentConfigured() || nativeProxySupportEnabled()) { + return; + } + + throw new Error( + "A proxy environment variable is set, but Node.js native proxy support is not enabled. Set NODE_USE_ENV_PROXY=1 for this action step.", + ); +} + // Configure the default settings for GitHub API requests export default request.defaults({ headers: { "user-agent": "actions/create-github-app-token" }, diff --git a/main.js b/main.js index 486d67e..d8ebee4 100644 --- a/main.js +++ b/main.js @@ -5,7 +5,7 @@ import { createAppAuth } from "@octokit/auth-app"; import { getPermissionsFromInputs } from "./lib/get-permissions-from-inputs.js"; import { main } from "./lib/main.js"; -import request from "./lib/request.js"; +import request, { ensureNativeProxySupport } from "./lib/request.js"; if (!process.env.GITHUB_REPOSITORY) { throw new Error("GITHUB_REPOSITORY missing, must be set to '/'"); @@ -15,31 +15,37 @@ if (!process.env.GITHUB_REPOSITORY_OWNER) { throw new Error("GITHUB_REPOSITORY_OWNER missing, must be set to ''"); } -const appId = core.getInput("app-id"); -const privateKey = core.getInput("private-key"); -const owner = core.getInput("owner"); -const repositories = core - .getInput("repositories") - .split(/[\n,]+/) - .map((s) => s.trim()) - .filter((x) => x !== ""); +async function run() { + ensureNativeProxySupport(); -const skipTokenRevoke = core.getBooleanInput("skip-token-revoke"); + const appId = core.getInput("app-id"); + const privateKey = core.getInput("private-key"); + const owner = core.getInput("owner"); + const repositories = core + .getInput("repositories") + .split(/[\n,]+/) + .map((s) => s.trim()) + .filter((x) => x !== ""); -const permissions = getPermissionsFromInputs(process.env); + const skipTokenRevoke = core.getBooleanInput("skip-token-revoke"); + + const permissions = getPermissionsFromInputs(process.env); + + return main( + appId, + privateKey, + owner, + repositories, + permissions, + core, + createAppAuth, + request, + skipTokenRevoke, + ); +} // Export promise for testing -export default main( - appId, - privateKey, - owner, - repositories, - permissions, - core, - createAppAuth, - request, - skipTokenRevoke, -).catch((error) => { +export default run().catch((error) => { /* c8 ignore next 3 */ console.error(error); core.setFailed(error.message); diff --git a/post.js b/post.js index feea045..41eb1e5 100644 --- a/post.js +++ b/post.js @@ -3,9 +3,15 @@ import * as core from "@actions/core"; import { post } from "./lib/post.js"; -import request from "./lib/request.js"; +import request, { ensureNativeProxySupport } from "./lib/request.js"; -post(core, request).catch((error) => { +async function run() { + ensureNativeProxySupport(); + + return post(core, request); +} + +run().catch((error) => { /* c8 ignore next 3 */ console.error(error); core.setFailed(error.message); diff --git a/tests/index.js b/tests/index.js index f300270..3e2d19e 100644 --- a/tests/index.js +++ b/tests/index.js @@ -21,6 +21,14 @@ for (const file of testFiles) { const env = { GITHUB_OUTPUT: undefined, GITHUB_STATE: undefined, + HTTP_PROXY: undefined, + HTTPS_PROXY: undefined, + http_proxy: undefined, + https_proxy: undefined, + NO_PROXY: undefined, + no_proxy: undefined, + NODE_OPTIONS: undefined, + NODE_USE_ENV_PROXY: undefined, }; const { stderr, stdout } = await execa("node", [`tests/${file}`], { env }); t.snapshot(stderr, "stderr"); diff --git a/tests/main-proxy-requires-native-support.test.js b/tests/main-proxy-requires-native-support.test.js new file mode 100644 index 0000000..c274764 --- /dev/null +++ b/tests/main-proxy-requires-native-support.test.js @@ -0,0 +1,14 @@ +process.env.GITHUB_REPOSITORY = "actions/create-github-app-token"; +process.env.GITHUB_REPOSITORY_OWNER = "actions"; +process.env.HTTPS_PROXY = "http://127.0.0.1:3128"; + +const originalConsoleError = console.error; +console.error = (...args) => { + originalConsoleError( + ...args.map((arg) => (arg instanceof Error ? arg.message : arg)), + ); +}; + +await import("../main.js"); +await new Promise((resolve) => setImmediate(resolve)); +process.exitCode = 0; diff --git a/tests/post-proxy-requires-native-support.test.js b/tests/post-proxy-requires-native-support.test.js new file mode 100644 index 0000000..cff0433 --- /dev/null +++ b/tests/post-proxy-requires-native-support.test.js @@ -0,0 +1,13 @@ +process.env["INPUT_GITHUB-API-URL"] = "https://api.github.com"; +process.env.HTTPS_PROXY = "http://127.0.0.1:3128"; + +const originalConsoleError = console.error; +console.error = (...args) => { + originalConsoleError( + ...args.map((arg) => (arg instanceof Error ? arg.message : arg)), + ); +}; + +await import("../post.js"); +await new Promise((resolve) => setImmediate(resolve)); +process.exitCode = 0; diff --git a/tests/snapshots/index.js.md b/tests/snapshots/index.js.md index e419536..0bd0f49 100644 --- a/tests/snapshots/index.js.md +++ b/tests/snapshots/index.js.md @@ -82,6 +82,16 @@ Generated by [AVA](https://avajs.dev). POST /app/installations/123456/access_tokens␊ {"repositories":["create-github-app-token"]}` +## main-proxy-requires-native-support.test.js + +> stderr + + 'A proxy environment variable is set, but Node.js native proxy support is not enabled. Set NODE_USE_ENV_PROXY=1 for this action step.' + +> stdout + + '::error::A proxy environment variable is set, but Node.js native proxy support is not enabled. Set NODE_USE_ENV_PROXY=1 for this action step.' + ## main-repo-skew.test.js > stderr @@ -333,6 +343,16 @@ Generated by [AVA](https://avajs.dev). POST /app/installations/123456/access_tokens␊ {"repositories":["create-github-app-token"],"permissions":{"issues":"write","pull_requests":"read"}}` +## post-proxy-requires-native-support.test.js + +> stderr + + 'A proxy environment variable is set, but Node.js native proxy support is not enabled. Set NODE_USE_ENV_PROXY=1 for this action step.' + +> stdout + + '::error::A proxy environment variable is set, but Node.js native proxy support is not enabled. Set NODE_USE_ENV_PROXY=1 for this action step.' + ## post-revoke-token-fail-response.test.js > stderr diff --git a/tests/snapshots/index.js.snap b/tests/snapshots/index.js.snap index e66c3d5..8be0324 100644 Binary files a/tests/snapshots/index.js.snap and b/tests/snapshots/index.js.snap differ