An open API service indexing awesome lists of open source software.

https://github.com/gr2m/github-webhook-relay

Receive webhooks from a GitHub repository via WebSocket
https://github.com/gr2m/github-webhook-relay

Last synced: about 1 year ago
JSON representation

Receive webhooks from a GitHub repository via WebSocket

Awesome Lists containing this project

README

          

# `github-webhook-relay`

> Receive webhooks from a GitHub repository via WebSocket
>
> **Warning**
> Receiving webhooks via websockets is currently in [private beta](https://github.blog/changelog/2022-11-16-webhook-forwarding-in-the-github-cli-public-beta/)

A Node.js library that uses the same APIs as the [`gh webhook` plugin](https://github.com/cli/gh-webhook) for the [GitHub CLI](https://cli.github.com/).

## Usage

The `createHookToken` option needs to be set to a [token with the `admin:repo_hook` and/or `admin:org_hook` scope](https://github.com/settings/tokens/new?scopes=admin:repo_hook,admin:org_hook&description=github-webhook-relay), depending on which you want to create.

### Minimal example

```js
import WebhookRelay from "github-webhook-relay";

const relay = new WebhookRelay({
owner: "gr2m",
repo: "github-webhooks-relay",
events: ["issues"],
createHookToken: process.env.GITHUB_TOKEN,
});

relay.on("webhook", ({ id, name, payload, signature, headers }) => {
console.log("received webhook: %s", name);
});

relay.on("error", (error) => {
console.log("error: %s", error);
});

relay.start();
```

### Use with Octokit

If you want to relay webhooks to a GitHub App, you can use [github-app-webhook-relay](https://github.com/gr2m/github-app-webhook-relay).

```js
import { App, Octokit } from "octokit";
import WebhookRelay from "github-webhook-relay";

const MyOctokit = Octokit.defaults({
userAgent: "my-app/1.2.3",
});

const app = new App({
appId: process.env.APP_ID,
privateKey: process.env.APP_PRIVATE_KEY,
webhooks: {
secret: process.env.APP_WEBHOOK_SECRET,
},
Octokit: MyOctokit,
});

app.webhooks.on("issues.opened", async ({ octokit }) => {
const { data: comment } = await octokit.request(
"POST /repos/{owner}/{repo}/issues/{issue_number}/comments",
{
owner: payload.repository.owner.login,
repo: payload.repository.name,
issue_number: payload.issue.number,
body: "Hello, world!",
}
);

console.log("[app] Comment created: %s", comment.html_url);
});

const relay = new WebhookRelay({
owner: "gr2m",
repo: "github-webhooks-relay",
events: ["issues"],
octokit: new MyOctokit({ auth: process.env.GITHUB_TOKEN }),
});

relay.on("webhook", app.webhooks.verifyAndReceive);
```

## API

### Constructor

```js
const relay = new WebhookRelay(options);
```




name


type


description






options.owner


string

**Required**. The account name of the GitHub user or organization.




options.repo


string

When set, the webhook will be created for the repository. When not set, the webhook will be created for the organization. Note that user-level webhooks are not supported by GitHub, so `options.owner` must be an organization.




options.events


string[]

**Required**. The list of events that the webhook should subscribe to. For a list of supported event names, see [the GitHub docs](https://docs.github.com/en/developers/webhooks-and-events/webhooks/webhook-events-and-payloads).




options.createHookToken


string

**Required unless `options.octokit` is set**. Access token to create the repository webhook. The token needs to have the `admin:repo_hook` scope. ([create a personal access token](https://github.com/settings/tokens/new?scopes=admin:repo_hook&description=github-webhook-relay)).




options.octokit


octokit

**Required unless `options.createHookToken` is set**. `octokit` is an instance of [`@octokit/core`](https://github.com/octokit/core.js/#readme) or a compatible constructor such as [`octokit`'s `Octokit`](https://github.com/octokit/octokit.js#octokit-api-client).




options.webhookSecret


string

The secret used to sign the webhook payloads. Defaults to no secret.


### `relay.on()`

```js
relay.on(eventName, callback);
```




name


type


description






eventName


string

**Required**. Supported events are

1. `webhook` - emitted when a webhook is received
1. `start` - emitted when the relay is started
1. `stop` - emitted when the relay is stopped
1. `error` - emitted when an error occurs




callback


function

**Required**. The event handler.

When `eventName` is `webhook`, the callback is called with an object with the following properties:

- `id` - the webhook delivery GUID
- `name` - the name of the event
- `body` - the webhook payload as string
- `signature` - the signature of the webhook payload
- `headers` - the headers of the webhook request

No arguments are passed when `eventName` is set to `start` or `stop`.

When `eventName` is `error`, the callback is called with an error object.

†The webhook payload is passed as is in case the signature needs to be verified again. Parsing the JSON and later stringifying it again bight result in a signature mismatch.


### `relay.start()`

```js
relay.start();
```

Creates the repository hook and connects to the GitHub webhook forwarding service.

### `relay.stop()`

```js
relay.start();
```

Disconnects from the GitHub webhook forwarding service and deletes the repository hook.

## How it works

When creating a repository webhook using [the `POST /repos/{owner}/{repo}/hooks` endpoint](https://docs.github.com/en/rest/webhooks/repos?apiVersion=2022-11-28#create-a-repository-webhook) and you set `name` to `"cli"` then the response body will include a `ws_url` key.

You can connect to the `ws_url` using a WebSocket client. Note that the handshake request requires an `Authorization` header which will have to be set to a personal access token. Using [the `ws` npm package](https://github.com/websockets/ws#readme) the code looks like this:

```js
const ws = new WebSocket(webSocketUrl, {
headers: {
Authorization: process.env.GITHUB_TOKEN,
},
});
```

Once the websocket is connected, the hook can be activated by setting the `active` property to `true` using [the `PATCH /repos/{owner}/{repo}/hooks/{hook_id}` endpoint](https://docs.github.com/en/rest/webhooks/repos?apiVersion=2022-11-28#update-a-repository-webhook).

Each Webhook request is received with a separate message. The message is a JSON string with a `Header` and `Body` keys. The `Body` is base64 encoded.

A response has to be sent back within 10s, otherwise the webhook request will be canceled and time out. No further message will be sent until a response is sent back. The response has to be a JSON string as well with the keys `Status`, `Header`, and `Body`. The `Body` value needs to be base64 encoded.

Disconnecting from the websocket will automatically delete the repository webhook.

## Contributing

Please see [CONTRIBUTING.md](CONTRIBUTING.md).

## See also

- [`github-webhook-relay-cli`](https://github.com/gr2m/github-webhook-relay-cli/#readme) - CLI version of this library
- [`github-app-webhook-relay`](https://github.com/gr2m/github-app-webhook-relay/#readme) - Webhook relay for GitHub Apps

## License

[ISC](LICENSE)