Skip to content

Commit

Permalink
style: prettier
Browse files Browse the repository at this point in the history
To stay consistent with `probot` and `@octokit/*` repositories
  • Loading branch information
gr2m committed Nov 5, 2020
1 parent 5f68c28 commit e3aa18f
Show file tree
Hide file tree
Showing 8 changed files with 118 additions and 1,061 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ jobs:
test:
strategy:
matrix:
node: ['12.x', '10.x', '8.x']
node: ["12.x", "10.x", "8.x"]

runs-on: ubuntu-latest

Expand All @@ -32,4 +32,4 @@ jobs:
- run: npm ci

# Run tests
- run: npm test
- run: npm test
11 changes: 8 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
## AWS Lambda Extension for Probot

A [Probot](https://github.com/probot/probot) extension to make it easier to run your Probot Apps in AWS Lambda.

## Usage
Expand All @@ -9,23 +10,27 @@ $ npm install @probot/serverless-lambda

```javascript
// handler.js
const { serverless } = require('@probot/serverless-lambda')
const appFn = require('./')
module.exports.probot = serverless(appFn)
const { serverless } = require("@probot/serverless-lambda");
const appFn = require("./");
module.exports.probot = serverless(appFn);
```

## Configuration

This package moves the functionality of `probot run` into a handler suitable for usage on AWS Lambda + API Gateway. Follow the documentation on [Environment Configuration](https://probot.github.io/docs/configuration/) to setup your app's environment variables. You can add these to `.env`, but for security reasons you may want to use the [AWS CLI](https://aws.amazon.com/cli/) or [Serverless Framework](https://github.com/serverless/serverless) to set Environment Variables for the function so you don't have to include any secrets in the deployed package.

For the private key, since AWS environment variables cannot be multiline strings, you could [Base64 encode](https://nodejs.org/api/buffer.html#buffer_buffers_and_character_encodings) the `.pem` file you get from the GitHub App or use [KMS](https://aws.amazon.com/kms/) to encrypt and store the key.

## Differences from `probot run`

#### Local Development

Since Lambda functions do not start a normal node process, the best way we've found to test this out locally is to use [`serverless-offline`](https://github.com/dherault/serverless-offline). This plugin for the serverless framework emulates AWS Lambda and API Gateway on your local machine, allowing you to continue working from `https://localhost:3000/probot` before deploying your function to AWS.

#### Long running tasks

Some Probot Apps that depend on long running processes or intervals will not work with this extension. This is due to the inherent architecture of serverless functions, which are designed to respond to events and stop running as quickly as possible. For longer running apps we recommend using [other deployment options](https://probot.github.io/docs/deployment).

#### If you use [HTTP routes](https://probot.github.io/docs/http/) or [WEBHOOK_PATH](https://probot.github.io/docs/configuration/)

This extension is designed primarily for receiving webhooks from GitHub and responding back as a GitHub App. If you are using [HTTP Routes](https://probot.github.io/docs/http/) in your app to serve additional pages, you should take a look at [`serverless-http`](https://github.com/dougmoscrop/serverless-http), which can be used with Probot's [express server](https://github.com/probot/probot/blob/master/src/server.ts) by wrapping `probot.server`.
84 changes: 42 additions & 42 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,108 +1,108 @@
const { Probot } = require('probot')
const { resolve } = require('probot/lib/helpers/resolve-app-function')
const { findPrivateKey } = require('probot/lib/helpers/get-private-key')
const { template } = require('./views/probot')
const { Probot } = require("probot");
const { resolve } = require("probot/lib/helpers/resolve-app-function");
const { findPrivateKey } = require("probot/lib/helpers/get-private-key");
const { template } = require("./views/probot");

let probot
let probot;

const loadProbot = (appFn) => {
probot =
probot ||
new Probot({
id: process.env.APP_ID,
secret: process.env.WEBHOOK_SECRET,
privateKey: findPrivateKey()
})
privateKey: findPrivateKey(),
});

if (typeof appFn === 'string') {
appFn = resolve(appFn)
if (typeof appFn === "string") {
appFn = resolve(appFn);
}

probot.load(appFn)
probot.load(appFn);

return probot
}
return probot;
};

const lowerCaseKeys = (obj = {}) =>
Object.keys(obj).reduce(
(accumulator, key) =>
Object.assign(accumulator, { [key.toLocaleLowerCase()]: obj[key] }),
{}
)
);

module.exports.serverless = (appFn) => {
return async (event, context) => {
// 🤖 A friendly homepage if there isn't a payload
if (event.httpMethod === 'GET' && event.path === '/probot') {
if (event.httpMethod === "GET" && event.path === "/probot") {
const res = {
statusCode: 200,
headers: {
'Content-Type': 'text/html'
"Content-Type": "text/html",
},
body: template
}
return res
body: template,
};
return res;
}

// Otherwise let's listen handle the payload
probot = probot || loadProbot(appFn)
probot = probot || loadProbot(appFn);

// Ends function immediately after callback
context.callbackWaitsForEmptyEventLoop = false
context.callbackWaitsForEmptyEventLoop = false;

// Determine incoming webhook event type
const headers = lowerCaseKeys(event.headers)
const e = headers['x-github-event']
const headers = lowerCaseKeys(event.headers);
const e = headers["x-github-event"];
if (!e) {
return {
statusCode: 400,
body: 'X-Github-Event header is missing'
}
body: "X-Github-Event header is missing",
};
}

// If body is expected to be base64 encoded, decode it and continue
if (event.isBase64Encoded) {
event.body = Buffer.from(event.body, 'base64').toString('utf8')
event.body = Buffer.from(event.body, "base64").toString("utf8");
}

// Convert the payload to an Object if API Gateway stringifies it
event.body =
typeof event.body === 'string' ? JSON.parse(event.body) : event.body
typeof event.body === "string" ? JSON.parse(event.body) : event.body;

// Bail for null body
if (!event.body) {
return {
statusCode: 400,
body: 'Event body is null.'
}
body: "Event body is null.",
};
}

// Do the thing
console.log(
`Received event ${e}${event.body.action ? '.' + event.body.action : ''}`
)
`Received event ${e}${event.body.action ? "." + event.body.action : ""}`
);
if (event) {
try {
await probot.receive({
name: e,
payload: event.body
})
payload: event.body,
});
return {
statusCode: 200,
body: JSON.stringify({
message: `Received ${e}.${event.body.action}`
})
}
message: `Received ${e}.${event.body.action}`,
}),
};
} catch (err) {
console.error(err)
console.error(err);
return {
statusCode: 500,
body: JSON.stringify(err)
}
body: JSON.stringify(err),
};
}
} else {
console.error({ event, context })
throw new Error('unknown error')
console.error({ event, context });
throw new Error("unknown error");
}
}
}
};
};
Loading

0 comments on commit e3aa18f

Please sign in to comment.