Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: support tokens scoped to multiple repositories within organization #46

Merged
merged 29 commits into from
Oct 4, 2023
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
12bf248
support token scope to multiple repos
timreimherr Sep 20, 2023
9c2fe6b
update documentation
timreimherr Sep 20, 2023
4e0d015
if owner not set default to current repo owner
timreimherr Sep 21, 2023
151c72e
default to all repos if none are supplied
timreimherr Sep 21, 2023
84c746a
tests for lib/main.js
timreimherr Sep 21, 2023
98d3657
remove jest test
timreimherr Sep 21, 2023
63a98a7
Update documentation
timreimherr Sep 21, 2023
8f5a382
Merge branch 'main' into main
gr2m Sep 21, 2023
a12bbe4
Merge branch 'main' into main
gr2m Sep 21, 2023
fb1cbf7
allow for 'owner' to be empty
timreimherr Sep 22, 2023
c21d2ca
scope according to input
timreimherr Sep 22, 2023
6d39deb
Update documentation
timreimherr Sep 22, 2023
90239ca
clarify documentation
timreimherr Sep 22, 2023
3cfbd0e
change action name for publishing
timreimherr Sep 22, 2023
e8a138f
fix action name
timreimherr Sep 25, 2023
7c7676d
build: dist/main.cjs
gr2m Sep 29, 2023
4b133dc
Merge branch 'main' into main
parkerbxyz Oct 3, 2023
68894b6
update main
gr2m Sep 29, 2023
02c936f
build update for testing
gr2m Oct 3, 2023
73f98bd
Update README.md
gr2m Oct 3, 2023
91b880c
Update action.yml
gr2m Oct 3, 2023
80484a9
build(package): lock file
gr2m Oct 3, 2023
aa7595e
build files for testing (after updating dependencies)
gr2m Oct 3, 2023
2df34b8
Use sentence case in comments for consistency
parkerbxyz Oct 3, 2023
13b24f0
Remove language codes from GitHub Docs URLs
parkerbxyz Oct 3, 2023
9dcf16e
Move note to a dedicated section
parkerbxyz Oct 3, 2023
dad2c36
Reword step 1
parkerbxyz Oct 3, 2023
0a057cb
Update example usage headers
parkerbxyz Oct 3, 2023
7c0311c
Update lib/main.js
gr2m Oct 4, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 16 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ In order to use this action, you need to:
1. [Register new GitHub App](https://docs.github.com/apps/creating-github-apps/setting-up-a-github-app/creating-a-github-app)
2. [Store the App's ID in your repository environment variables](https://docs.github.com/actions/learn-github-actions/variables#defining-configuration-variables-for-multiple-workflows) (example: `APP_ID`)
3. [Store the App's private key in your repository secrets](https://docs.github.com/actions/security-guides/encrypted-secrets?tool=webui#creating-encrypted-secrets-for-a-repository) (example: `PRIVATE_KEY`)
4. We recommend that `owner` be set to `env.GITHUB_REPOSITORY_OWNER` (See [environment variables](https://docs.github.com/en/actions/learn-github-actions/variables#default-environment-variables))
gr2m marked this conversation as resolved.
Show resolved Hide resolved

### Minimal usage
gr2m marked this conversation as resolved.
Show resolved Hide resolved

Expand All @@ -24,6 +25,8 @@ jobs:
with:
app_id: ${{ vars.APP_ID }}
private_key: ${{ secrets.PRIVATE_KEY }}
owener: ${{ github.repository_owner }}
repositories: repo1,repo2
gr2m marked this conversation as resolved.
Show resolved Hide resolved
- uses: peter-evans/create-or-update-comment@v3
with:
token: ${{ steps.app-token.outputs.token }}
Expand All @@ -46,6 +49,10 @@ jobs:
# required
app_id: ${{ vars.APP_ID }}
private_key: ${{ secrets.PRIVATE_KEY }}
# optional - if not used, defaults to current repository owner
owner: ${{ github.repository_owner }}
# optional - if not used, defaults to all repositories in the organization where the app is installed
repositories: repo1,repo2
gr2m marked this conversation as resolved.
Show resolved Hide resolved
- uses: actions/checkout@v3
with:
token: ${{ steps.app-token.outputs.token }}
Expand All @@ -67,6 +74,14 @@ jobs:

**Required:** GitHub App private key.

### `owner`

**Optional:** GitHub App installation owner. Defaults to the current repository owner.

### `repositories`

**Optional:** Comma-separated list of repositories to grant access to. Defaults to the current repository.

## Outputs

### `token`
Expand All @@ -77,7 +92,7 @@ GitHub App installation access token.

The action creates an installation access token using [the `POST /app/installations/{installation_id}/access_tokens` endpoint](https://docs.github.com/rest/apps/apps?apiVersion=2022-11-28#create-an-installation-access-token-for-an-app). By default,

1. The token is scoped to the current repository.
1. The token is scoped to the current repository or the repositories given.
2. The token inherits all the installation's permissions.
3. The token is set as output `token` which can be used in subsequent steps.
4. The token is revoked in the `post` step of the action, which means it cannot be passed to another job.
Expand Down
6 changes: 6 additions & 0 deletions action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,12 @@ inputs:
private_key:
description: "GitHub App private key"
required: true
owner:
description: "GitHub App owner (defaults to current repository owner)"
required: false
repositories:
description: "Repositories to install the GitHub App on (defaults to current repository)"
gr2m marked this conversation as resolved.
Show resolved Hide resolved
required: false
outputs:
token:
description: "GitHub installation access token"
Expand Down
4 changes: 3 additions & 1 deletion dist/post.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -2956,8 +2956,10 @@ var import_core = __toESM(require_core(), 1);
// lib/post.js
async function post(core2, request2) {
const token = core2.getState("token");
if (!token)
if (!token) {
core2.info("Token is not set");
return;
}
await request2("DELETE /installation/token", {
headers: {
authorization: `token ${token}`
Expand Down
34 changes: 22 additions & 12 deletions lib/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,23 @@
/**
* @param {string} appId
* @param {string} privateKey
* @param {string} repository
* @param {string} org
* @param {string} repositories
* @param {import("@actions/core")} core
* @param {import("@octokit/auth-app").createAppAuth} createAppAuth
* @param {import("@octokit/request").request} request
*/
export async function main(
appId,
privateKey,
repository,
org,
repositories,
core,
createAppAuth,
request
) {
// Get owner and repo name from GITHUB_REPOSITORY
const [owner, repo] = repository.split("/");

const repos = repositories.split(",").map((repo) => repo.trim());

const auth = createAppAuth({
appId,
Expand All @@ -32,23 +34,31 @@ export async function main(
// Get the installation ID
// https://docs.github.com/en/rest/apps/apps?apiVersion=2022-11-28#get-a-repository-installation-for-the-authenticated-app
const { data: installation } = await request(
"GET /repos/{owner}/{repo}/installation",
"GET /orgs/{org}/installation",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if we need to get the installation ID for the current owner, we should keep doing it using GET /repos/{owner}/{repo}/installation. Because if the owner is set to something else, we need to check two endpoints, because the owner can be either an org or a user:

  1. GET /orgs/{org}/installation
  2. GET /users/{username}/installation

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See my suggested change at timreimherr#1

{
owner,
repo,
org,
headers: {
authorization: `bearer ${appAuthentication.token}`,
},
}
);

// Create a new installation token
const authentication = await auth({
type: "installation",
installationId: installation.id,
repositoryNames: [repo],
});
let authentication;

if (repositories.length == 0) {
authentication = await auth({
type: "installation",
installationId: installation.id,
});
} else {
authentication = await auth({
type: "installation",
installationId: installation.id,
repositoryNames: repos,
});
}

// Register the token with the runner as a secret to ensure it is masked in logs
core.setSecret(authentication.token);

Expand Down
11 changes: 8 additions & 3 deletions main.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,20 @@ if (!process.env.GITHUB_REPOSITORY) {
throw new Error("GITHUB_REPOSITORY missing, must be set to '<owner>/<repo>'");
}

if (!process.env.GITHUB_REPOSITORY_OWNER) {
throw new Error("GITHUB_REPOSITORY_OWNER missing, must be set to '<owner>'");
}

const appId = core.getInput("app_id");
const privateKey = core.getInput("private_key");

const repository = process.env.GITHUB_REPOSITORY;
const owner = core.getInput("owner") == "" ? process.env.GITHUB_REPOSITORY_OWNER?.trim() : core.getInput("owner");
const repositories = core.getInput("repositories");

main(
appId,
privateKey,
repository,
owner,
repositories,
core,
createAppAuth,
request.defaults({
Expand Down