Skip to content

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

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

Merged
merged 29 commits into from
Oct 4, 2023
Merged
Show file tree
Hide file tree
Changes from 20 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
79 changes: 77 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ In order to use this action, you need to:
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`)

### Minimal usage
### Create token for current repository

```yaml
on: [issues]
Expand Down Expand Up @@ -57,6 +57,73 @@ jobs:
github_token: ${{ steps.app-token.outputs.token }}
```

### Create token for all repositories in current owner's installation

```yaml
on: [workflow_dispatch]

jobs:
hello-world:
runs-on: ubuntu-latest
steps:
- uses: actions/create-github-app-token@v1
id: app-token
with:
app_id: ${{ vars.APP_ID }}
private_key: ${{ secrets.PRIVATE_KEY }}
owner: ${{ github.repository_owner }}
- uses: peter-evans/create-or-update-comment@v3
with:
token: ${{ steps.app-token.outputs.token }}
issue-number: ${{ github.event.issue.number }}
body: "Hello, World!"
```

### Create a token for selected repositories in current owner's installation

```yaml
on: [issues]

jobs:
hello-world:
runs-on: ubuntu-latest
steps:
- uses: actions/create-github-app-token@v1
id: app-token
with:
app_id: ${{ vars.APP_ID }}
private_key: ${{ secrets.PRIVATE_KEY }}
owner: ${{ github.repository_owner }}
repositories: "repo1,repo2"
- uses: peter-evans/create-or-update-comment@v3
with:
token: ${{ steps.app-token.outputs.token }}
issue-number: ${{ github.event.issue.number }}
body: "Hello, World!"
```

### Create token for all repositories in another owner's installation

```yaml
on: [issues]

jobs:
hello-world:
runs-on: ubuntu-latest
steps:
- uses: actions/create-github-app-token@v1
id: app-token
with:
app_id: ${{ vars.APP_ID }}
private_key: ${{ secrets.PRIVATE_KEY }}
owner: another-owner
- uses: peter-evans/create-or-update-comment@v3
with:
token: ${{ steps.app-token.outputs.token }}
issue-number: ${{ github.event.issue.number }}
body: "Hello, World!"
```

## Inputs

### `app_id`
Expand All @@ -67,6 +134,14 @@ jobs:

**Required:** GitHub App private key.

### `owner`

**Optional:** GitHub App installation owner. If empty, defaults to the current repository owner.

### `repositories`

**Optional:** Comma-separated list of repositories to grant access to. If 'owner' is set and 'repositories' is empty then the access will be scoped to all repositories in the provided repository owner's installation. If 'owner' and 'repositories' are empty then the access will be scoped to only the current repository.

## Outputs

### `token`
Expand All @@ -77,7 +152,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)"
required: false
outputs:
token:
description: "GitHub installation access token"
Expand Down
100 changes: 77 additions & 23 deletions dist/main.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -1602,7 +1602,7 @@ var require_oidc_utils = __commonJS({

Error Code : ${error.statusCode}

Error Message: ${error.message}`);
Error Message: ${error.result.message}`);
});
const id_token = (_a = res.result) === null || _a === void 0 ? void 0 : _a.value;
if (!id_token) {
Expand Down Expand Up @@ -2793,7 +2793,7 @@ var require_dist_node5 = __commonJS({
module2.exports = __toCommonJS2(dist_src_exports);
var import_endpoint = require_dist_node2();
var import_universal_user_agent = require_dist_node();
var VERSION = "8.1.2";
var VERSION = "8.1.1";
var import_is_plain_object = require_is_plain_object();
var import_request_error = require_dist_node4();
function getBufferResponse(response) {
Expand Down Expand Up @@ -14795,7 +14795,7 @@ var require_dist_node12 = __commonJS({
data: {
token,
expires_at: expiresAt,
repositories,
repositories: repositories2,
permissions: permissionsOptional,
repository_selection: repositorySelectionOptional,
single_file: singleFileName
Expand All @@ -14814,8 +14814,8 @@ var require_dist_node12 = __commonJS({
});
const permissions = permissionsOptional || {};
const repositorySelection = repositorySelectionOptional || "all";
const repositoryIds = repositories ? repositories.map((r) => r.id) : void 0;
const repositoryNames = repositories ? repositories.map((repo) => repo.name) : void 0;
const repositoryIds = repositories2 ? repositories2.map((r) => r.id) : void 0;
const repositoryNames = repositories2 ? repositories2.map((repo) => repo.name) : void 0;
const createdAt = (/* @__PURE__ */ new Date()).toISOString();
await set(state.cache, optionsWithInstallationTokenFromState, {
token,
Expand Down Expand Up @@ -14984,7 +14984,7 @@ var require_dist_node12 = __commonJS({
return sendRequestWithRetries(state, request2, options, createdAt, retries);
}
}
var VERSION = "6.0.1";
var VERSION = "6.0.0";
var import_auth_oauth_user2 = require_dist_node9();
function createAppAuth2(options) {
if (!options.appId) {
Expand Down Expand Up @@ -15043,8 +15043,37 @@ var import_core = __toESM(require_core(), 1);
var import_auth_app = __toESM(require_dist_node12(), 1);

// lib/main.js
async function main(appId2, privateKey2, repository2, core2, createAppAuth2, request2) {
const [owner, repo] = repository2.split("/");
async function main(appId2, privateKey2, owner2, repositories2, core2, createAppAuth2, request2) {
let parsedOwner = "";
let parsedRepositoryNames = "";
if (!owner2 && !repositories2) {
[parsedOwner, parsedRepositoryNames] = String(
process.env.GITHUB_REPOSITORY
).split("/");
core2.info(
`owner and repositories not set, creating token for the current repository ("${parsedRepositoryNames}")`
);
}
if (owner2 && !repositories2) {
parsedOwner = owner2;
core2.info(
`repositories not set, creating token for all repositories for given owner "${owner2}"`
);
}
if (!owner2 && repositories2) {
parsedOwner = String(process.env.GITHUB_REPOSITORY_OWNER);
parsedRepositoryNames = repositories2;
core2.info(
`owner not set, creating owner for given repositories "${repositories2}" in current owner ("${parsedOwner}")`
);
}
if (owner2 && repositories2) {
parsedOwner = owner2;
parsedRepositoryNames = repositories2;
core2.info(
`owner and repositories set, creating token for repositories "${repositories2}" owned by "${owner2}"`
);
}
const auth = createAppAuth2({
appId: appId2,
privateKey: privateKey2,
Expand All @@ -15053,23 +15082,43 @@ async function main(appId2, privateKey2, repository2, core2, createAppAuth2, req
const appAuthentication = await auth({
type: "app"
});
const { data: installation } = await request2(
"GET /repos/{owner}/{repo}/installation",
{
owner,
repo,
let authentication;
if (parsedRepositoryNames) {
const response = await request2("GET /repos/{owner}/{repo}/installation", {
owner: parsedOwner,
repo: parsedRepositoryNames.split(",")[0],
headers: {
authorization: `bearer ${appAuthentication.token}`
}
}
);
const authentication = await auth({
type: "installation",
installationId: installation.id,
repositoryNames: [repo]
});
core2.setSecret(authentication.token);
});
authentication = await auth({
type: "installation",
installationId: response.data.id,
repositoryNames: parsedRepositoryNames.split(",")
});
} else {
const response = await request2("GET /orgs/{org}/installation", {
org: parsedOwner,
headers: {
authorization: `bearer ${appAuthentication.token}`
}
}).catch((error) => {
if (error.status !== 404)
throw error;
return request2("GET /users/{username}/installation", {
username: parsedOwner,
headers: {
authorization: `bearer ${appAuthentication.token}`
}
});
});
authentication = await auth({
type: "installation",
installationId: response.data.id
});
}
core2.setOutput("token", authentication.token);
core2.setSecret(authentication.token);
core2.saveState("token", authentication.token);
}

Expand All @@ -15086,13 +15135,18 @@ var request_default = import_request.request.defaults({
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>'");
}
var appId = import_core.default.getInput("app_id");
var privateKey = import_core.default.getInput("private_key");
var repository = process.env.GITHUB_REPOSITORY;
var owner = import_core.default.getInput("owner");
var repositories = import_core.default.getInput("repositories");
main(
appId,
privateKey,
repository,
owner,
repositories,
import_core.default,
import_auth_app.createAppAuth,
request_default.defaults({
Expand Down
4 changes: 2 additions & 2 deletions dist/post.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -1602,7 +1602,7 @@ var require_oidc_utils = __commonJS({

Error Code : ${error.statusCode}

Error Message: ${error.message}`);
Error Message: ${error.result.message}`);
});
const id_token = (_a = res.result) === null || _a === void 0 ? void 0 : _a.value;
if (!id_token) {
Expand Down Expand Up @@ -2793,7 +2793,7 @@ var require_dist_node5 = __commonJS({
module2.exports = __toCommonJS2(dist_src_exports);
var import_endpoint = require_dist_node2();
var import_universal_user_agent = require_dist_node();
var VERSION = "8.1.2";
var VERSION = "8.1.1";
var import_is_plain_object = require_is_plain_object();
var import_request_error = require_dist_node4();
function getBufferResponse(response) {
Expand Down
Loading