diff --git a/README.md b/README.md index 9637ad3..f129b79 100644 --- a/README.md +++ b/README.md @@ -163,7 +163,9 @@ jobs: app-id: ${{ vars.APP_ID }} private-key: ${{ secrets.PRIVATE_KEY }} owner: ${{ github.repository_owner }} - repositories: "repo1,repo2" + repositories: | + repo1 + repo2 - uses: peter-evans/create-or-update-comment@v3 with: token: ${{ steps.app-token.outputs.token }} @@ -302,7 +304,7 @@ steps: ### `repositories` -**Optional:** Comma-separated list of repositories to grant access to. +**Optional:** Comma or newline-separated list of repositories to grant access to. > [!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. diff --git a/action.yml b/action.yml index 09cc8fa..184c59f 100644 --- a/action.yml +++ b/action.yml @@ -23,7 +23,7 @@ inputs: description: "The owner of the GitHub App installation (defaults to current repository owner)" required: false repositories: - description: "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 skip-token-revoke: description: "If truthy, the token will not be revoked when the current job is complete" diff --git a/dist/main.cjs b/dist/main.cjs index 515e8be..eb2283b 100644 --- a/dist/main.cjs +++ b/dist/main.cjs @@ -39700,33 +39700,35 @@ async function pRetry(input, options) { // lib/main.js async function main(appId2, privateKey2, owner2, repositories2, core3, createAppAuth2, request2, skipTokenRevoke2) { let parsedOwner = ""; - let parsedRepositoryNames = ""; - if (!owner2 && !repositories2) { - [parsedOwner, parsedRepositoryNames] = String( + let parsedRepositoryNames = []; + if (!owner2 && repositories2.length === 0) { + const [owner3, repo] = String( process.env.GITHUB_REPOSITORY ).split("/"); + parsedOwner = owner3; + parsedRepositoryNames = [repo]; core3.info( - `owner and repositories not set, creating token for the current repository ("${parsedRepositoryNames}")` + `owner and repositories not set, creating token for the current repository ("${repo}")` ); } - if (owner2 && !repositories2) { + if (owner2 && repositories2.length === 0) { parsedOwner = owner2; core3.info( `repositories not set, creating token for all repositories for given owner "${owner2}"` ); } - if (!owner2 && repositories2) { + if (!owner2 && repositories2.length > 0) { parsedOwner = String(process.env.GITHUB_REPOSITORY_OWNER); parsedRepositoryNames = repositories2; core3.info( - `owner not set, creating owner for given repositories "${repositories2}" in current owner ("${parsedOwner}")` + `owner not set, creating owner for given repositories "${repositories2.join(",")}" in current owner ("${parsedOwner}")` ); } - if (owner2 && repositories2) { + if (owner2 && repositories2.length > 0) { parsedOwner = owner2; parsedRepositoryNames = repositories2; core3.info( - `owner and repositories set, creating token for repositories "${repositories2}" owned by "${owner2}"` + `owner and repositories set, creating token for repositories "${repositories2.join(",")}" owned by "${owner2}"` ); } const auth5 = createAppAuth2({ @@ -39735,11 +39737,11 @@ async function main(appId2, privateKey2, owner2, repositories2, core3, createApp request: request2 }); let authentication, installationId, appSlug; - if (parsedRepositoryNames) { + if (parsedRepositoryNames.length > 0) { ({ authentication, installationId, appSlug } = await pRetry(() => getTokenFromRepository(request2, auth5, parsedOwner, parsedRepositoryNames), { onFailedAttempt: (error) => { core3.info( - `Failed to create token for "${parsedRepositoryNames}" (attempt ${error.attemptNumber}): ${error.message}` + `Failed to create token for "${parsedRepositoryNames.join(",")}" (attempt ${error.attemptNumber}): ${error.message}` ); }, retries: 3 @@ -39789,7 +39791,7 @@ async function getTokenFromOwner(request2, auth5, parsedOwner) { async function getTokenFromRepository(request2, auth5, parsedOwner, parsedRepositoryNames) { const response = await request2("GET /repos/{owner}/{repo}/installation", { owner: parsedOwner, - repo: parsedRepositoryNames.split(",")[0], + repo: parsedRepositoryNames[0], request: { hook: auth5.hook } @@ -39797,7 +39799,7 @@ async function getTokenFromRepository(request2, auth5, parsedOwner, parsedReposi const authentication = await auth5({ type: "installation", installationId: response.data.id, - repositoryNames: parsedRepositoryNames.split(",") + repositoryNames: parsedRepositoryNames }); const installationId = response.data.id; const appSlug = response.data["app_slug"]; @@ -39847,7 +39849,7 @@ if (!privateKey) { throw new Error("Input required and not supplied: private-key"); } var owner = import_core2.default.getInput("owner"); -var repositories = import_core2.default.getInput("repositories"); +var repositories = import_core2.default.getInput("repositories").split(/[\n,]+/).map((s) => s.trim()).filter((x) => x !== ""); var skipTokenRevoke = Boolean( import_core2.default.getInput("skip-token-revoke") || import_core2.default.getInput("skip_token_revoke") ); diff --git a/lib/main.js b/lib/main.js index 97443c0..bc15f95 100644 --- a/lib/main.js +++ b/lib/main.js @@ -5,7 +5,7 @@ import pRetry from "p-retry"; * @param {string} appId * @param {string} privateKey * @param {string} owner - * @param {string} repositories + * @param {string[]} repositories * @param {import("@actions/core")} core * @param {import("@octokit/auth-app").createAppAuth} createAppAuth * @param {import("@octokit/request").request} request @@ -22,21 +22,23 @@ export async function main( skipTokenRevoke ) { let parsedOwner = ""; - let parsedRepositoryNames = ""; + let parsedRepositoryNames = []; // If neither owner nor repositories are set, default to current repository - if (!owner && !repositories) { - [parsedOwner, parsedRepositoryNames] = String( + 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 ("${parsedRepositoryNames}")` + `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) { + if (owner && repositories.length === 0) { parsedOwner = owner; core.info( @@ -45,22 +47,22 @@ export async function main( } // If repositories are set, but no owner, default to `GITHUB_REPOSITORY_OWNER` - if (!owner && repositories) { + 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}" in current owner ("${parsedOwner}")` + `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) { + if (owner && repositories.length > 0) { parsedOwner = owner; parsedRepositoryNames = repositories; core.info( - `owner and repositories set, creating token for repositories "${repositories}" owned by "${owner}"` + `owner and repositories set, creating token for repositories "${repositories.join(',')}" owned by "${owner}"` ); } @@ -73,11 +75,11 @@ export async function main( let authentication, installationId, appSlug; // If at least one repository is set, get installation ID from that repository - if (parsedRepositoryNames) { + if (parsedRepositoryNames.length > 0) { ({ authentication, installationId, appSlug } = await pRetry(() => getTokenFromRepository(request, auth, parsedOwner, parsedRepositoryNames), { onFailedAttempt: (error) => { core.info( - `Failed to create token for "${parsedRepositoryNames}" (attempt ${error.attemptNumber}): ${error.message}` + `Failed to create token for "${parsedRepositoryNames.join(',')}" (attempt ${error.attemptNumber}): ${error.message}` ); }, retries: 3, @@ -144,7 +146,7 @@ async function getTokenFromRepository(request, auth, parsedOwner, parsedReposito // 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.split(",")[0], + repo: parsedRepositoryNames[0], request: { hook: auth.hook, }, @@ -154,11 +156,11 @@ async function getTokenFromRepository(request, auth, parsedOwner, parsedReposito const authentication = await auth({ type: "installation", installationId: response.data.id, - repositoryNames: parsedRepositoryNames.split(","), + repositoryNames: parsedRepositoryNames, }); const installationId = response.data.id; const appSlug = response.data['app_slug']; return { authentication, installationId, appSlug }; - } \ No newline at end of file +} diff --git a/main.js b/main.js index b4d919d..8011b51 100644 --- a/main.js +++ b/main.js @@ -25,7 +25,10 @@ if (!privateKey) { throw new Error("Input required and not supplied: private-key"); } const owner = core.getInput("owner"); -const repositories = core.getInput("repositories"); +const repositories = core.getInput("repositories") + .split(/[\n,]+/) + .map(s => s.trim()) + .filter(x => x !== ''); const skipTokenRevoke = Boolean( core.getInput("skip-token-revoke") || core.getInput("skip_token_revoke") diff --git a/package-lock.json b/package-lock.json index fef3933..332bbb1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "create-github-app-token", - "version": "1.10.3", + "version": "1.10.4", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "create-github-app-token", - "version": "1.10.3", + "version": "1.10.4", "license": "MIT", "dependencies": { "@actions/core": "^1.10.1", diff --git a/tests/main-token-get-owner-set-repo-set-to-many-newline.test.js b/tests/main-token-get-owner-set-repo-set-to-many-newline.test.js new file mode 100644 index 0000000..ad03c67 --- /dev/null +++ b/tests/main-token-get-owner-set-repo-set-to-many-newline.test.js @@ -0,0 +1,9 @@ +import { test } from "./main.js"; + +// Verify `main` successfully obtains a token when the `owner` and `repositories` inputs are set (and the latter is a list of repos). +await test(() => { + process.env.INPUT_OWNER = process.env.GITHUB_REPOSITORY_OWNER; + const currentRepoName = process.env.GITHUB_REPOSITORY.split("/")[1]; + // Intentional unnecessary whitespace to test parsing to array + process.env.INPUT_REPOSITORIES = `\n ${currentRepoName}\ntoolkit \n\n checkout \n`; +}); diff --git a/tests/main-token-get-owner-set-repo-set-to-many.test.js b/tests/main-token-get-owner-set-repo-set-to-many.test.js index 173fcf4..074e53b 100644 --- a/tests/main-token-get-owner-set-repo-set-to-many.test.js +++ b/tests/main-token-get-owner-set-repo-set-to-many.test.js @@ -4,5 +4,6 @@ import { test } from "./main.js"; await test(() => { process.env.INPUT_OWNER = process.env.GITHUB_REPOSITORY_OWNER; const currentRepoName = process.env.GITHUB_REPOSITORY.split("/")[1]; - process.env.INPUT_REPOSITORIES = `${currentRepoName},toolkit`; + // Intentional unnecessary whitespace to test parsing to array + process.env.INPUT_REPOSITORIES = ` ${currentRepoName}, toolkit ,checkout`; }); diff --git a/tests/snapshots/index.js.md b/tests/snapshots/index.js.md index 023d104..76d4b78 100644 --- a/tests/snapshots/index.js.md +++ b/tests/snapshots/index.js.md @@ -134,6 +134,25 @@ Generated by [AVA](https://avajs.dev). ::save-state name=token::ghs_16C7e42F292c6912E7710c838347Ae178B4a␊ ::save-state name=expiresAt::2016-07-11T22:14:10Z` +## main-token-get-owner-set-repo-set-to-many-newline.test.js + +> stderr + + '' + +> stdout + + `owner and repositories set, creating token for repositories "create-github-app-token,toolkit,checkout" owned by "actions"␊ + ::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` + ## main-token-get-owner-set-repo-set-to-many.test.js > stderr @@ -142,7 +161,7 @@ Generated by [AVA](https://avajs.dev). > stdout - `owner and repositories set, creating token for repositories "create-github-app-token,toolkit" owned by "actions"␊ + `owner and repositories set, creating token for repositories "create-github-app-token,toolkit,checkout" owned by "actions"␊ ::add-mask::ghs_16C7e42F292c6912E7710c838347Ae178B4a␊ ␊ ::set-output name=token::ghs_16C7e42F292c6912E7710c838347Ae178B4a␊ diff --git a/tests/snapshots/index.js.snap b/tests/snapshots/index.js.snap index 89369aa..9956084 100644 Binary files a/tests/snapshots/index.js.snap and b/tests/snapshots/index.js.snap differ