feat(setup): support registration using an existing GitHub app (#1345)

This commit is contained in:
Max Baumann 2020-09-18 18:17:51 +02:00 committed by GitHub
parent 31fdd619ac
commit d907bbda0c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 174 additions and 7 deletions

View File

@ -1,5 +1,7 @@
import bodyParser from "body-parser";
import { exec } from "child_process";
import { Request, Response } from "express";
import updateDotenv from "update-dotenv";
import { Application } from "../application";
import { ManifestCreation } from "../manifest-creation";
@ -27,12 +29,7 @@ export const setupAppFactory = (
printWelcomeMessage(app, host, port);
route.get("/probot", async (req, res) => {
const protocols = req.headers["x-forwarded-proto"] || req.protocol;
const protocol =
typeof protocols === "string" ? protocols.split(",")[0] : protocols[0];
const host = req.headers["x-forwarded-host"] || req.get("host");
const baseUrl = `${protocol}://${host}`;
const baseUrl = getBaseUrl(req);
const pkg = setup.pkg;
const manifest = setup.getManifest(pkg, baseUrl);
const createAppUrl = setup.createAppUrl;
@ -56,6 +53,26 @@ export const setupAppFactory = (
res.redirect(`${response}/installations/new`);
});
route.get("/probot/import", async (_req, res) => {
const { WEBHOOK_PROXY_URL, GHE_HOST } = process.env;
const GH_HOST = `https://${GHE_HOST ?? "github.com"}`;
res.render("import.hbs", { WEBHOOK_PROXY_URL, GH_HOST });
});
route.post("/probot/import", bodyParser.json(), async (req, res) => {
const { appId, pem, webhook_secret } = req.body;
if (!appId || !pem || !webhook_secret) {
res.status(400).send("appId and/or pem and/or webhook_secret missing");
return;
}
updateDotenv({
APP_ID: appId,
PRIVATE_KEY: `"${pem}"`,
WEBHOOK_SECRET: webhook_secret,
});
res.end();
});
route.get("/probot/success", async (req, res) => {
res.render("success.hbs");
});
@ -87,3 +104,12 @@ function printWelcomeMessage(
app.log.info(line);
});
}
function getBaseUrl(req: Request): string {
const protocols = req.headers["x-forwarded-proto"] || req.protocol;
const protocol =
typeof protocols === "string" ? protocols.split(",")[0] : protocols[0];
const host = req.headers["x-forwarded-host"] || req.get("host");
const baseUrl = `${protocol}://${host}`;
return baseUrl;
}

View File

@ -16,3 +16,20 @@ Array [
],
]
`;
exports[`Setup app POST /probot/import updates .env 1`] = `
Array [
Array [
Object {
"WEBHOOK_PROXY_URL": "mocked proxy URL",
},
],
Array [
Object {
"APP_ID": "foo",
"PRIVATE_KEY": "\\"bar\\"",
"WEBHOOK_SECRET": "baz",
},
],
]
`;

View File

@ -100,6 +100,45 @@ describe("Setup app", () => {
});
});
describe("GET /probot/import", () => {
it("renders import.hbs", async () => {
await request(probot.server).get("/probot/import").expect(200);
});
});
describe("POST /probot/import", () => {
it("updates .env", async () => {
const body = JSON.stringify({
appId: "foo",
pem: "bar",
webhook_secret: "baz",
});
await request(probot.server)
.post("/probot/import")
.set("content-type", "application/json")
.send(body)
.expect(200)
.expect("");
expect(updateDotenv.mock.calls).toMatchSnapshot();
});
it("400 when keys are missing", async () => {
const body = JSON.stringify({
appId: "foo",
/* no pem */
webhook_secret: "baz",
});
await request(probot.server)
.post("/probot/import")
.set("content-type", "application/json")
.send(body)
.expect(400);
});
});
describe("GET /probot/success", () => {
it("returns a 200 response", async () => {
await request(probot.server).get("/probot/success").expect(200);

84
views/import.hbs Normal file
View File

@ -0,0 +1,84 @@
<!DOCTYPE html>
<html lang="en" class="height-full">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Import {{#if pkg.name }}{{ pkg.name }}{{else}}Your App{{/if}} | built with Probot</title>
<link rel="icon" href="/probot/static/probot-head.png">
<link rel="stylesheet" href="/probot/static/primer.css">
</head>
<body class="bg-gray-light">
<div class="d-flex flex-column flex-justify-center flex-items-center text-center height-full py-6">
<a href="/probot"><img src="/probot/static/robot.svg" alt="Probot Logo" width="100" class="mb-6"></a>
<div class="box-shadow rounded-2 border p-6 bg-white">
<h2>Use existing Github App</h2>
<br>
<h3>Step 1:</h3>
<p class="d-block mt-2">
Replace your app's Webhook URL with <br>
<b>{{ WEBHOOK_PROXY_URL }}</b>
</p>
<a class="d-block mt-2" href="{{ GH_HOST }}/settings/apps" target="__blank" rel="noreferrer">
You can do it here
</a>
<br>
<h3>Step 2:</h3>
<p class="mt-2">Fill out this form</p>
<form onsubmit="return onSubmit(event) || false">
<label class="d-block mt-2" for="appId">App Id</label>
<input class="form-control width-full" type="text" required="true" id="appId" name="appId"><br>
<label class="d-block mt-3" for="whs">Webhook secret (required!)</label>
<input class="form-control width-full" type="password" required="true" id="whs" name="whs"><br>
<label class="d-block mt-3" for="pem">Private Key</label>
<input class="form-control width-full m-2" type="file" accept=".pem" required="true" id="pem"
name="pem">
<br>
<button class="btn btn-outline m-2" type="submit">Submit</button>
</form>
</div>
<div class="mt-4">
<h4 class="alt-h4 text-gray-light">Need help?</h4>
<div class="d-flex flex-justify-center mt-2">
<a href="https://probot.github.io/docs/" class="btn btn-outline mr-2">Documentation</a>
<a href="https://probot-slackin.herokuapp.com/" class="btn btn-outline">Chat on Slack</a>
</div>
</div>
</div>
<script>
function onSubmit(e) {
e.preventDefault();
const idEl = document.getElementById('appId');
const appId = idEl.value;
const secretEl = document.getElementById('whs');
const webhook_secret = secretEl.value;
const fileEl = document.getElementById('pem');
const file = fileEl.files[0];
file.text().then((text) => fetch('', {
method: 'POST',
headers: { 'content-type': 'application/json' },
body: JSON.stringify({ appId, pem: text, webhook_secret })
})).then((r) => {
if (r.ok) {
location.replace('/probot/success');
}
}).catch((e) => alert(e));
return false;
}
</script>
</body>
</html>

View File

@ -31,8 +31,9 @@
<p>To start building a GitHub App, you'll need to register a new app on GitHub.</p>
<br>
<form action="{{ createAppUrl }}" method="post" target="_blank">
<form action="{{ createAppUrl }}" method="post" target="_blank" class="d-flex flex-items-center">
<button class="btn btn-outline" name="manifest" id="manifest" value='{{ manifest }}' >Register GitHub App</button>
<a href="/probot/import" class="ml-2">or use an existing Github App</a>
</form>
</div>
</div>