Skip to content

Commit

Permalink
fix: handle error if app is not installed (#3)
Browse files Browse the repository at this point in the history
* fix: handle error if app is not installed

* chore(docs): small fixes

* chore(ci): fix error
  • Loading branch information
npalm authored Mar 10, 2021
1 parent cbe0c01 commit a807139
Show file tree
Hide file tree
Showing 6 changed files with 90 additions and 59 deletions.
11 changes: 6 additions & 5 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
name: 'CI'
on:
push:
branches:
- main
- develop
pull_request:

jobs:
Expand Down Expand Up @@ -34,7 +31,7 @@ jobs:
file_pattern: dist/*

release:
if: github.event_name != 'pull_request' && contains('refs/heads/develop, refs/heads/main', github.ref)
if: github.event_name != 'pull_request'
needs: build
runs-on: ubuntu-latest
steps:
Expand All @@ -50,10 +47,14 @@ jobs:
auth_type: installation
org: philips-software

- name: Extract branch name
run: echo ::set-output name=short_ref::${GITHUB_REF#refs/*/}
id: branch

- name: Dry run release
env:
GITHUB_TOKEN: ${{ steps.app.outputs.token }}
run: yarn && yarn run release -d
run: yarn && yarn run release -d -b ${{ steps.branch.outputs.short_ref }}

- name: Release
if: github.event_name != 'pull_request' && contains('refs/heads/main', github.ref)
Expand Down
5 changes: 0 additions & 5 deletions Dockerfile

This file was deleted.

40 changes: 23 additions & 17 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,29 @@ A GitHub action that obtains a token for an app authorization which can used ins

Before the action can be used the APP has to be installed on the subject repository or in the organization scope.

## Action input parameters

| Parameter | Description | required | default |
| ---------------------- | :------------------------------------------------------------ | -------- | ------- |
| app_id | Application id | true | |
| app_base64_private_key | Application SSH private key as base64 | true | |
| auth_type | Authorization type | false | app |
| org | Name of the org, if not provided will be read from the event. | false | |
## Inputs

## Action output parameters
| parameter | description | required | default |
| ---------------------- | ------------------------------------------------------------- | -------- | ------- |
| app_id | Application ID | `true` | |
| app_base64_private_key | Application SSH private key as base64 | `true` | |
| auth_type | Authorization type, either app or installation | `false` | app |
| org | Name of the org, if not provided will be read from the event. | `false` | |

| Parameter | Description |
| --------- | :------------------------- |
| token | GitHub authorization token |

### Example
## Outputs

| parameter | description |
| --------- | ----------------- |
| token | Application token |


## Runs

This action is an `node12` action.

## Usage

Below an example snippet how to use the action.

Expand All @@ -46,12 +53,11 @@ Standard commands such as lint, test and build are available via yarn. Check [pa

### Test locally

To run the action core function locally a simple wrapper is provided in [src/local.ts]. To run the wrapper ensure you have set the environment variables `INPUT_APP_REPO`, `APP_ID` and `APP_BASE64_PRIVATE_KEY`. `APP_REPO` takes a `philips-internal` repository name. Next run `yarn run watch`.

Running locally requires you hav have an app in your org that is installed on one or more repositories. To run the local ensure you have set the environment variables:

const appType = core.getInput('auth_type', { required: true }) as 'installation' | 'app';
const appId = Number(core.getInput('app_id', { required: true }));
const appBase64PrivateKey = core.getInput('app_base64_private_key
- `INPUT_AUTH_TYPE` : `app` or `token`
- `INPUT_APP_ID` and `INPUT_APP_BASE64_PRIVATE_KEY`: The app id and app ssh key.
- `INPUT_ORG`: The org in which the app is installed.


**Example:**
Expand Down
29 changes: 17 additions & 12 deletions dist/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -15282,18 +15282,23 @@ exports.getAppToken = getAppToken;
const getAppInstallationToken = (privateKey, appId, org) => __awaiter(void 0, void 0, void 0, function* () {
const appToken = yield exports.getAppToken(privateKey, appId);
const octokit = new rest_1.Octokit({ auth: appToken });
const installationId = yield octokit.apps.getOrgInstallation({
org,
});
const authAuthInstallation = auth_app_1.createAppAuth({
appId,
privateKey,
installationId: installationId.data.id,
});
const auth = yield authAuthInstallation({
type: 'installation',
});
return auth.token;
try {
const installationId = yield octokit.apps.getOrgInstallation({
org,
});
const authAuthInstallation = auth_app_1.createAppAuth({
appId,
privateKey,
installationId: installationId.data.id,
});
const auth = yield authAuthInstallation({
type: 'installation',
});
return auth.token;
}
catch (e) {
throw new Error(`Cannot find installation for app with id: ${appId}. Did you installed your app in a repo?`);
}
});
exports.getAppInstallationToken = getAppInstallationToken;
const getToken = (parameters) => __awaiter(void 0, void 0, void 0, function* () {
Expand Down
31 changes: 26 additions & 5 deletions src/auth.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,11 @@ const defaultParameters: Parameters = {
base64PrivateKey: testBase64PrivateKey,
};

function mockGetOrgInstallation(id: number): void {
nock('https://api.github.com').persist().get(`/orgs/${testOrg}/installation`).reply(200, { id });
function mockGetOrgInstallation(id: number, org: string): void {
const responseCode = id < 0 ? 403 : 200;
nock('https://api.github.com').persist().get(`/orgs/${org}/installation`).reply(responseCode, { id });
}

function mockAppInstallationToken(id: string): void {
nock('https://api.github.com')
.persist()
Expand All @@ -32,7 +34,7 @@ describe('Auth type installation', () => {
beforeEach(() => {
jest.restoreAllMocks();
jest.clearAllMocks();
mockGetOrgInstallation(47);
mockGetOrgInstallation(47, testOrg);
mockAppInstallationToken('47');
});

Expand All @@ -53,11 +55,30 @@ describe('Auth type installation', () => {
});
});

describe('Auth type app', () => {
describe('App not installed.', () => {
beforeEach(() => {
jest.restoreAllMocks();
jest.clearAllMocks();
jest.resetAllMocks();
mockGetOrgInstallation(-1, 'org-without-apps');
mockAppInstallationToken('-1');
});

test('Should throw exception for app that is not installed..', async () => {
await expect(
getToken({
...defaultParameters,
org: 'org-without-apps',
}),
).rejects.toThrow();
});
});

describe('Auth type 222', () => {
beforeEach(() => {
jest.restoreAllMocks();
jest.clearAllMocks();
mockGetOrgInstallation(47);
mockGetOrgInstallation(47, testOrg);
});

test('Should throw exception for invalid private key.', async () => {
Expand Down
33 changes: 18 additions & 15 deletions src/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,21 +24,24 @@ export const getAppInstallationToken = async (privateKey: string, appId: number,
const appToken = await getAppToken(privateKey, appId);
const octokit = new Octokit({ auth: appToken });

const installationId = await octokit.apps.getOrgInstallation({
org,
});

const authAuthInstallation = createAppAuth({
appId,
privateKey,
installationId: installationId.data.id,
});

const auth = await authAuthInstallation({
type: 'installation',
});

return auth.token;
try {
const installationId = await octokit.apps.getOrgInstallation({
org,
});
const authAuthInstallation = createAppAuth({
appId,
privateKey,
installationId: installationId.data.id,
});

const auth = await authAuthInstallation({
type: 'installation',
});

return auth.token;
} catch (e) {
throw new Error(`Cannot find installation for app with id: ${appId}. Did you installed your app in a repo?`);
}
};

export const getToken = async (parameters: Parameters): Promise<string | undefined> => {
Expand Down

0 comments on commit a807139

Please sign in to comment.