Skip to content
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

feat: Migrate to modern dependencies for v13 #1821

Merged
merged 71 commits into from Nov 14, 2023

Conversation

AaronDewes
Copy link
Member

@AaronDewes AaronDewes commented Apr 21, 2023

This PR updates all dependencies to modern versions. It should not cause any user-facing changes apart from what's already done in v13.

Also, probot run can now load ESM modules 🚀

AaronDewes and others added 11 commits April 21, 2023 16:52
There are still some failures, but less is failing now
This migrates to modern Octokit, Node, Jest and other dependency versions.
@octokit/webhooks now requires a content-type header
The fetch API currently breaks nock
@wolfy1339
Copy link
Collaborator

This should also resolve #1784

Note that @octokit/webhooks only accepts string payloads as of v11

@wolfy1339
Copy link
Collaborator

wolfy1339 commented Apr 21, 2023

You will most likely want to update @octokit/webhooks-types and @octokit/webhooks-examples

Would it be best to change the default webhooks path to be /webhooks to avoid any kind of issues between the HTTP server and the webhooks?

@AaronDewes
Copy link
Member Author

You will most likely want to update @octokit/webhooks-types and @octokit/webhooks-examples

Yes, I'll update these too, thanks!

Would it be best to change the default webhooks path to be /webhooks to avoid any kind of issues between the HTTP server and the webhooks?

I'd prefer not too, because then every Probot user would have to change their webhook path in the apps settings on GitHub, which would make migration more complicated and could lead to unexpected issues after migration if people forget.

But if you want to keep the current behaviour of @octokit/webhooks, I guess we'll make this change.

I'd also like to hear @gr2m's opinion about this.

@AaronDewes
Copy link
Member Author

You will most likely want to update @octokit/webhooks-types and @octokit/webhooks-examples

Actually, these are already at their latest version here, they seem to be using a different versioning scheme.

@gr2m
Copy link
Contributor

gr2m commented May 16, 2023

hey just want to say thanks for all the work you put into this! I'm slowly catching up with notifications, but I'll need more time to review this one and the related issues in @octokit. Please bear with me

@wolfy1339
Copy link
Collaborator

FYI, the npm problems with the Octokit modules used in Probot have been fixed, simply update to the latest version.

The next major versions, which are in progress, will drop support for Node v14 and v16, and drops node-fetch.

@pixelass

This comment was marked as off-topic.

@gr2m
Copy link
Contributor

gr2m commented May 27, 2023

@pixelass sorry after I collapsed your comment I see that your PR and this one here by @AaronDewes has actually quite an overlap, like the upgrade of Jest to the latest version.

Can you two coordinate? I'm happy to do the transition to ESM, it's going to happen with Octokit too, hopefully sooner than later.

I would wait for the breaking changes coming from Octokit which will drop the dependency on node-fetch and let us use much more modern Node APIs. We can drop support for Node 16 and below, we only need to support Node 18 and 20 as part of the change.

Thank you both <3 And we all have to give a big shoutout to @wolfy1339 who is driving the upgrade of all the @octokit/* packages.

@pixelass
Copy link

pixelass commented May 27, 2023

@gr2m thanks for the quick reply. I'm happy to help ge this done. Sadly the heroku app for slack seems to be down (https://probot-slackin.herokuapp.com/) so I'll post here.

@AaronDewes nice to meet you. 👋

@gr2m
Copy link
Contributor

gr2m commented May 27, 2023

Sadly the heroku app for slack seems to be down

yeah we no longer use it. We use https://github.com/probot/probot/discussions/ instead, but in this case communicating here is perfectly fine. Sometime we setup sync calls via zoom or similar, feel free to do that if it's helpful

GitHub
Explore the GitHub Discussions forum for probot probot. Discuss code, ask questions & collaborate with the developer community.

@AaronDewes
Copy link
Member Author

@pixelass Are you on Discord, Telegram or Signal? Maybe we can talk there?

@AaronDewes
Copy link
Member Author

I've experimented with ESM, but Nock does not yet work with it (And also not with fetch()), which breaks the tests

@wolfy1339
Copy link
Collaborator

Instead of nock try fetch-mock

@wolfy1339
Copy link
Collaborator

Instead of adding the environment variables, and calling jest directly in the workflows, put it in the npm scripts instead.

That way anybody using the scripts can get jest running smoothly on their local machine

Co-authored-by: Aras Abbasi <aras.abbasi@googlemail.com>
test/probot.test.ts Outdated Show resolved Hide resolved
@Uzlopak

This comment was marked as outdated.

@Uzlopak

This comment was marked as outdated.

Copy link
Collaborator

@Uzlopak Uzlopak left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

I have some nitpicks, but we can do them in this PR or maybe solve them in follow up PRs.

I would prefer to merge this PR, to simply have it done after being 7 months work in progress ;).

Nice job @AaronDewes

Copy link
Collaborator

@Uzlopak Uzlopak left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Last nitpick

src/types.ts Outdated Show resolved Hide resolved

const stubAppFnPath = require.resolve("./fixtures/plugin/stub-plugin");
const stubAppFnPath = require.resolve("./fixtures/plugin/stub-plugin.ts");
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this require be removed?
require isn't defined in an ESM environment, and all other instances have been removed

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WIth what should it be replaced?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this require be removed? require isn't defined in an ESM environment, and all other instances have been removed

We're not ESM-Only yet, but we can remove it in the future. We can use createRequire as a workaround once we're truly ESM-only. The ESM-native import.meta.resolve was only stabilized in Node v20.6.0.

@Uzlopak
Copy link
Collaborator

Uzlopak commented Nov 14, 2023

How about removing handlebars?
From 2f8764b93fbc0df02362be35a8dfef122ece83da Mon Sep 17 00:00:00 2001
From: uzlopak <aras.abbasi@googlemail.com>
Date: Tue, 14 Nov 2023 02:02:12 +0100
Subject: [PATCH] remove handlebars

---
 package-lock.json                             |  24 ++--
 package.json                                  |   1 -
 src/apps/default.ts                           |  12 +-
 src/apps/setup.ts                             |  39 ++++--
 src/server/server.ts                          |   9 --
 .../import.handlebars => src/views/import.ts  |  20 ++-
 .../probot.handlebars => src/views/probot.ts  |  37 +++--
 views/setup.handlebars => src/views/setup.ts  |  43 ++++--
 .../views/success.ts                          |   8 +-
 test/apps/__snapshots__/default.test.ts.snap  |  70 ++++++++++
 test/apps/__snapshots__/setup.test.ts.snap    | 127 +++++++++++++++++-
 test/apps/default.test.ts                     |   2 +
 test/apps/setup.test.ts                       |  22 ++-
 13 files changed, 336 insertions(+), 78 deletions(-)
 rename views/import.handlebars => src/views/import.ts (88%)
 rename views/probot.handlebars => src/views/probot.ts (62%)
 rename views/setup.handlebars => src/views/setup.ts (62%)
 rename views/success.handlebars => src/views/success.ts (89%)
 create mode 100644 test/apps/__snapshots__/default.test.ts.snap

diff --git a/package-lock.json b/package-lock.json
index de2cb615..62ad9512 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -26,7 +26,6 @@
         "dotenv": "^16.3.1",
         "eventsource": "^2.0.2",
         "express": "^4.18.2",
-        "express-handlebars": "^7.1.2",
         "ioredis": "^5.3.2",
         "js-yaml": "^4.1.0",
         "lru-cache": "^10.0.2",
@@ -4345,19 +4344,6 @@
         "node": ">= 0.10.0"
       }
     },
-    "node_modules/express-handlebars": {
-      "version": "7.1.2",
-      "resolved": "https://registry.npmjs.org/express-handlebars/-/express-handlebars-7.1.2.tgz",
-      "integrity": "sha512-ss9d3mBChOLTEtyfzXCsxlItUxpgS3i4cb/F70G6Q5ohQzmD12XB4x/Y9U6YboeeYBJZt7WQ5yUNu7ZSQ/EGyQ==",
-      "dependencies": {
-        "glob": "^10.3.3",
-        "graceful-fs": "^4.2.11",
-        "handlebars": "^4.7.8"
-      },
-      "engines": {
-        "node": ">=v16"
-      }
-    },
     "node_modules/express/node_modules/body-parser": {
       "version": "1.20.1",
       "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz",
@@ -4980,6 +4966,7 @@
       "version": "4.7.8",
       "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz",
       "integrity": "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==",
+      "dev": true,
       "dependencies": {
         "minimist": "^1.2.5",
         "neo-async": "^2.6.2",
@@ -6490,6 +6477,7 @@
       "version": "1.2.8",
       "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
       "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
+      "dev": true,
       "funding": {
         "url": "https://github.com/sponsors/ljharb"
       }
@@ -6607,7 +6595,8 @@
     "node_modules/neo-async": {
       "version": "2.6.2",
       "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz",
-      "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw=="
+      "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==",
+      "dev": true
     },
     "node_modules/nerf-dart": {
       "version": "1.0.0",
@@ -12011,6 +12000,7 @@
       "version": "0.6.1",
       "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
       "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+      "dev": true,
       "engines": {
         "node": ">=0.10.0"
       }
@@ -12829,6 +12819,7 @@
       "version": "3.17.4",
       "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.17.4.tgz",
       "integrity": "sha512-T9q82TJI9e/C1TAxYvfb16xO120tMVFZrGA3f9/P4424DNu6ypK103y0GPFVa17yotwSyZW5iYXgjYHkGrJW/g==",
+      "dev": true,
       "optional": true,
       "bin": {
         "uglifyjs": "bin/uglifyjs"
@@ -13268,7 +13259,8 @@
     "node_modules/wordwrap": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz",
-      "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q=="
+      "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==",
+      "dev": true
     },
     "node_modules/wrap-ansi": {
       "version": "8.1.0",
diff --git a/package.json b/package.json
index d63724a4..afb119df 100644
--- a/package.json
+++ b/package.json
@@ -54,7 +54,6 @@
     "dotenv": "^16.3.1",
     "eventsource": "^2.0.2",
     "express": "^4.18.2",
-    "express-handlebars": "^7.1.2",
     "ioredis": "^5.3.2",
     "js-yaml": "^4.1.0",
     "lru-cache": "^10.0.2",
diff --git a/src/apps/default.ts b/src/apps/default.ts
index 54de6cc4..bd15dd7b 100644
--- a/src/apps/default.ts
+++ b/src/apps/default.ts
@@ -2,6 +2,8 @@ import type { ApplicationFunctionOptions, Probot } from "../index";
 import { loadPackageJson } from "../helpers/load-package-json";
 import { resolve } from "path";
 
+import { probotView } from "../views/probot";
+
 export function defaultApp(
   _app: Probot,
   { getRouter, cwd = process.cwd() }: ApplicationFunctionOptions,
@@ -10,12 +12,16 @@ export function defaultApp(
     throw new Error("getRouter() is required for defaultApp");
   }
 
+  const pkg = loadPackageJson(resolve(cwd, "package.json"));
+  const probotViewRendered = probotView({
+    name: pkg.name,
+    version: pkg.version,
+    description: pkg.description,
+  });
   const router = getRouter();
 
   router.get("/probot", (_req, res) => {
-    const pkg = loadPackageJson(resolve(cwd, "package.json"));
-
-    res.render("probot.handlebars", pkg);
+    res.send(probotViewRendered);
   });
 
   router.get("/", (_req, res) => res.redirect("/probot"));
diff --git a/src/apps/setup.ts b/src/apps/setup.ts
index 18407e31..b33d149f 100644
--- a/src/apps/setup.ts
+++ b/src/apps/setup.ts
@@ -9,6 +9,10 @@ import { getLoggingMiddleware } from "../server/logging-middleware";
 import type { ApplicationFunctionOptions } from "../types";
 import { isProduction } from "../helpers/is-production";
 
+import { importView } from "../views/import";
+import { setupView } from "../views/setup";
+import { successView } from "../views/success";
+
 export const setupAppFactory = (
   host: string | undefined,
   port: number | undefined,
@@ -18,6 +22,7 @@ export const setupAppFactory = (
     { getRouter }: ApplicationFunctionOptions,
   ) {
     const setup: ManifestCreation = new ManifestCreation();
+    const pkg = setup.pkg;
 
     // If not on Glitch or Production, create a smee URL
     if (
@@ -43,11 +48,18 @@ export const setupAppFactory = (
 
     route.get("/probot", async (req, res) => {
       const baseUrl = getBaseUrl(req);
-      const pkg = setup.pkg;
       const manifest = setup.getManifest(pkg, baseUrl);
       const createAppUrl = setup.createAppUrl;
       // Pass the manifest to be POST'd
-      res.render("setup.handlebars", { pkg, createAppUrl, manifest });
+      res.send(
+        setupView({
+          name: pkg.name,
+          version: pkg.version,
+          description: pkg.description,
+          createAppUrl,
+          manifest,
+        }),
+      );
     });
 
     route.get("/probot/setup", async (req: Request, res: Response) => {
@@ -71,13 +83,20 @@ 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.handlebars", { WEBHOOK_PROXY_URL, GH_HOST });
+    const { WEBHOOK_PROXY_URL, GHE_HOST } = process.env;
+    const GH_HOST = `https://${GHE_HOST ?? "github.com"}`;
+
+    const importViewRendered = importView({
+      name: pkg.name,
+      WEBHOOK_PROXY_URL,
+      GH_HOST,
     });
 
-    route.post("/probot/import", express.json(), async (req, res) => {
+    route.get("/probot/import", (_req, res) => {
+      res.send(importViewRendered);
+    });
+
+    route.post("/probot/import", express.json(), (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");
@@ -92,8 +111,10 @@ export const setupAppFactory = (
       printRestartMessage(app);
     });
 
-    route.get("/probot/success", async (_req, res) => {
-      res.render("success.handlebars");
+    const successViewRendered = successView({ name: pkg.name });
+
+    route.get("/probot/success", (_req, res) => {
+      res.send(successViewRendered);
     });
 
     route.get("/", (_req, res) => res.redirect("/probot"));
diff --git a/src/server/server.ts b/src/server/server.ts
index cb41e8f3..91b12580 100644
--- a/src/server/server.ts
+++ b/src/server/server.ts
@@ -11,7 +11,6 @@ import { createWebhookProxy } from "../helpers/webhook-proxy";
 import { VERSION } from "../version";
 import type { ApplicationFunction, ServerOptions } from "../types";
 import { Probot } from "../";
-import { engine } from "express-handlebars";
 import EventSource from "eventsource";
 
 // the default path as defined in @octokit/webhooks
@@ -63,14 +62,6 @@ export class Server {
       }),
     );
 
-    this.expressApp.engine(
-      "handlebars",
-      engine({
-        defaultLayout: false,
-      }),
-    );
-    this.expressApp.set("view engine", "handlebars");
-    this.expressApp.set("views", join(__dirname, "..", "..", "views"));
     this.expressApp.get("/ping", (_req, res) => res.end("PONG"));
   }
 
diff --git a/views/import.handlebars b/src/views/import.ts
similarity index 88%
rename from views/import.handlebars
rename to src/views/import.ts
index 809c858f..aff1a33d 100644
--- a/views/import.handlebars
+++ b/src/views/import.ts
@@ -1,11 +1,20 @@
-<!DOCTYPE html>
+export function importView({
+  name,
+  GH_HOST,
+  WEBHOOK_PROXY_URL = "",
+}: {
+  name: string;
+  GH_HOST: string;
+  WEBHOOK_PROXY_URL?: string;
+}): string {
+  return `<!DOCTYPE html>
 <html lang="en" class="height-full" data-color-mode="auto" data-light-theme="light" data-dark-theme="dark">
 
 <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>
+    <title>Import ${name || "Your App"} | built with Probot</title>
     <link rel="icon" href="/probot/static/probot-head.png">
     <link rel="stylesheet" href="/probot/static/primer.css">
 </head>
@@ -20,9 +29,9 @@
             <h3>Step 1:</h3>
             <p class="d-block mt-2">
                 Replace your app's Webhook URL with <br>
-                <b>{{ WEBHOOK_PROXY_URL }}</b>
+                <b>${WEBHOOK_PROXY_URL}</b>
             </p>
-            <a class="d-block mt-2" href="{{ GH_HOST }}/settings/apps" target="__blank" rel="noreferrer">
+            <a class="d-block mt-2" href="${GH_HOST}/settings/apps" target="__blank" rel="noreferrer">
                 You can do it here
             </a>
 
@@ -81,4 +90,5 @@
     </script>
 </body>
 
-</html>
\ No newline at end of file
+</html>`;
+}
diff --git a/views/probot.handlebars b/src/views/probot.ts
similarity index 62%
rename from views/probot.handlebars
rename to src/views/probot.ts
index 80977a07..30ed5abd 100644
--- a/views/probot.handlebars
+++ b/src/views/probot.ts
@@ -1,10 +1,19 @@
-<!DOCTYPE html>
+export function probotView({
+  name,
+  description,
+  version,
+}: {
+  name: string;
+  description: string;
+  version: string;
+}): string {
+  return `<!DOCTYPE html>
 <html lang="en" class="height-full" data-color-mode="auto" data-light-theme="light" data-dark-theme="dark">
   <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>{{#if name }}{{ name }}{{else}}Your App{{/if}} | built with Probot</title>
+    <title>${name || "Your App"} | built with Probot</title>
     <link rel="icon" href="/probot/static/probot-head.png">
     <link rel="stylesheet" href="/probot/static/primer.css">
   </head>
@@ -13,17 +22,18 @@
       <img src="/probot/static/robot.svg" alt="Probot Logo" width="100" class="mb-6">
       <div class="box-shadow rounded-2 border p-6 bg-white">
         <h1>
-          Welcome to {{#if name }}{{ name }}{{else}}your Probot App{{/if}}
-          {{#if version}}
-            <span class="Label Label--outline v-align-middle ml-2 text-gray-light">v{{ version }}</span>
-          {{/if}}
-        </h1>
+          Welcome to ${name || "your Probot App"}
+${
+  version
+    ? `            <span class="Label Label--outline v-align-middle ml-2 text-gray-light">v${version}</span>\n`
+    : ""
+}        </h1>
 
-        {{#if description }}
-          <p>{{ description }}</p>
-        {{else}}
-          <p>This bot was built using <a href="https://github.com/probot/probot">Probot</a>, a framework for building GitHub Apps.</p>
-        {{/if}}
+          <p>${
+            description
+              ? description
+              : 'This bot was built using <a href="https://github.com/probot/probot">Probot</a>, a framework for building GitHub Apps.'
+          }</p>
       </div>
 
       <div class="mt-4">
@@ -35,4 +45,5 @@
       </div>
     </div>
   </body>
-</html>
+</html>`;
+}
diff --git a/views/setup.handlebars b/src/views/setup.ts
similarity index 62%
rename from views/setup.handlebars
rename to src/views/setup.ts
index d9e282f9..95a920d3 100644
--- a/views/setup.handlebars
+++ b/src/views/setup.ts
@@ -1,10 +1,24 @@
+export function setupView({
+  name,
+  description,
+  version,
+  createAppUrl,
+  manifest,
+}: {
+  name: string;
+  description: string;
+  version: string;
+  createAppUrl: string;
+  manifest: string;
+}): string {
+  return `<!DOCTYPE html>
 <!DOCTYPE html>
 <html lang="en" class="height-full" data-color-mode="auto" data-light-theme="light" data-dark-theme="dark">
   <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>Setup {{#if pkg.name }}{{ pkg.name }}{{else}}Your App{{/if}} | built with Probot</title>
+    <title>Setup ${name || "Your App"} | built with Probot</title>
     <link rel="icon" href="/probot/static/probot-head.png">
     <link rel="stylesheet" href="/probot/static/primer.css">
   </head>
@@ -13,17 +27,19 @@
       <img src="/probot/static/robot.svg" alt="Probot Logo" width="100" class="mb-6">
       <div class="box-shadow rounded-2 border p-6 bg-white">
         <h1>
-          Welcome to {{#if pkg.name }}{{ pkg.name }}{{else}}your Probot App{{/if}}
-          {{#if version}}
-            <span class="Label Label--outline v-align-middle ml-2 text-gray-light">v{{ pkg.version }}</span>
-          {{/if}}
+          Welcome to ${name || "your Probot App"}
+          ${
+            version
+              ? `<span class="Label Label--outline v-align-middle ml-2 text-gray-light">v${version}</span>`
+              : ""
+          }
         </h1>
 
-        {{#if pkg.description }}
-          <p>{{ pkg.description }}</p>
-        {{else}}
-          <p>This app was built using <a href="https://github.com/probot/probot">Probot</a>, a framework for building GitHub Apps.</p>
-        {{/if}}
+        <p>${
+          description
+            ? description
+            : 'This app was built using <a href="https://github.com/probot/probot">Probot</a>, a framework for building GitHub Apps.'
+        }</p>
 
         <div class="text-left mt-6">
           <h2 class="alt-h3 mb-2">Getting Started</h2>
@@ -31,8 +47,8 @@
           <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" class="d-flex flex-items-center">
-            <button class="btn btn-outline" name="manifest" id="manifest" value='{{ manifest }}' >Register GitHub App</button>
+          <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>
@@ -47,4 +63,5 @@
       </div>
     </div>
   </body>
-</html>
+</html>`;
+}
diff --git a/views/success.handlebars b/src/views/success.ts
similarity index 89%
rename from views/success.handlebars
rename to src/views/success.ts
index 6ea5d23d..50d88c34 100644
--- a/views/success.handlebars
+++ b/src/views/success.ts
@@ -1,10 +1,11 @@
-<!DOCTYPE html>
+export function successView({ name }: { name: string }): string {
+  return `<!DOCTYPE html>
 <html lang="en" class="height-full" data-color-mode="auto" data-light-theme="light" data-dark-theme="dark">
   <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>Setup {{#if pkg.name }}{{ pkg.name }}{{else}}Your App{{/if}} | built with Probot</title>
+    <title>Setup ${name || "Your App"} | built with Probot</title>
     <link rel="icon" href="/probot/static/probot-head.png">
     <link rel="stylesheet" href="/probot/static/primer.css">
   </head>
@@ -28,4 +29,5 @@
       </div>
     </div>
   </body>
-</html>
+</html>`;
+}
diff --git a/test/apps/__snapshots__/default.test.ts.snap b/test/apps/__snapshots__/default.test.ts.snap
new file mode 100644
index 00000000..4f7dfdbf
--- /dev/null
+++ b/test/apps/__snapshots__/default.test.ts.snap
@@ -0,0 +1,70 @@
+// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
+
+exports[`default app > GET /probot > get info from package.json > returns the correct HTML with values 1`] = `
+"<!DOCTYPE html>
+<html lang=\\"en\\" class=\\"height-full\\" data-color-mode=\\"auto\\" data-light-theme=\\"light\\" data-dark-theme=\\"dark\\">
+  <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>probot | built with Probot</title>
+    <link rel=\\"icon\\" href=\\"/probot/static/probot-head.png\\">
+    <link rel=\\"stylesheet\\" href=\\"/probot/static/primer.css\\">
+  </head>
+  <body class=\\"height-full bg-gray-light\\">
+    <div class=\\"d-flex flex-column flex-justify-center flex-items-center text-center height-full\\">
+      <img src=\\"/probot/static/robot.svg\\" alt=\\"Probot Logo\\" width=\\"100\\" class=\\"mb-6\\">
+      <div class=\\"box-shadow rounded-2 border p-6 bg-white\\">
+        <h1>
+          Welcome to probot
+            <span class=\\"Label Label--outline v-align-middle ml-2 text-gray-light\\">v0.0.0-development</span>
+        </h1>
+
+          <p>A framework for building GitHub Apps to automate and improve your workflow</p>
+      </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>
+  </body>
+</html>"
+`;
+
+exports[`default app > GET /probot > get info from package.json > returns the correct HTML without values 1`] = `
+"<!DOCTYPE html>
+<html lang=\\"en\\" class=\\"height-full\\" data-color-mode=\\"auto\\" data-light-theme=\\"light\\" data-dark-theme=\\"dark\\">
+  <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>Your App | built with Probot</title>
+    <link rel=\\"icon\\" href=\\"/probot/static/probot-head.png\\">
+    <link rel=\\"stylesheet\\" href=\\"/probot/static/primer.css\\">
+  </head>
+  <body class=\\"height-full bg-gray-light\\">
+    <div class=\\"d-flex flex-column flex-justify-center flex-items-center text-center height-full\\">
+      <img src=\\"/probot/static/robot.svg\\" alt=\\"Probot Logo\\" width=\\"100\\" class=\\"mb-6\\">
+      <div class=\\"box-shadow rounded-2 border p-6 bg-white\\">
+        <h1>
+          Welcome to your Probot App
+        </h1>
+
+          <p>This bot was built using <a href=\\"https://github.com/probot/probot\\">Probot</a>, a framework for building GitHub Apps.</p>
+      </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>
+  </body>
+</html>"
+`;
diff --git a/test/apps/__snapshots__/setup.test.ts.snap b/test/apps/__snapshots__/setup.test.ts.snap
index fefb1bd7..43e8265e 100644
--- a/test/apps/__snapshots__/setup.test.ts.snap
+++ b/test/apps/__snapshots__/setup.test.ts.snap
@@ -1,6 +1,95 @@
 // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
 
-exports[`Setup app > GET /probot/setup > returns a redirect 1`] = `
+exports[`Setup app > GET /probot/import > renders importView 1`] = `
+"<!DOCTYPE html>
+<html lang=\\"en\\" class=\\"height-full\\" data-color-mode=\\"auto\\" data-light-theme=\\"light\\" data-dark-theme=\\"dark\\">
+
+<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 probot | 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></b>
+            </p>
+            <a class=\\"d-block mt-2\\" href=\\"https://github.com/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>"
+`;
+
+exports[`Setup app > GET /probot/setup > returns a redirect 1`] = `"Found. Redirecting to /apps/my-app/installations/new"`;
+
+exports[`Setup app > GET /probot/setup > returns a redirect 2`] = `
 [
   [
     {
@@ -24,6 +113,42 @@ exports[`Setup app > GET /probot/setup > returns a redirect 1`] = `
 ]
 `;
 
+exports[`Setup app > GET /probot/success > returns a 200 response 1`] = `
+"<!DOCTYPE html>
+<html lang=\\"en\\" class=\\"height-full\\" data-color-mode=\\"auto\\" data-light-theme=\\"light\\" data-dark-theme=\\"dark\\">
+  <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>Setup probot | built with Probot</title>
+    <link rel=\\"icon\\" href=\\"/probot/static/probot-head.png\\">
+    <link rel=\\"stylesheet\\" href=\\"/probot/static/primer.css\\">
+  </head>
+  <body class=\\"height-full bg-gray-light\\">
+    <div class=\\"d-flex flex-column flex-justify-center flex-items-center text-center height-full\\">
+      <img src=\\"/probot/static/robot.svg\\" alt=\\"Probot Logo\\" width=\\"100\\" class=\\"mb-6\\">
+      <div class=\\"box-shadow rounded-2 border p-6 bg-white\\">
+        <div class=\\"text-center\\">
+          <h1 class=\\"alt-h3 mb-2\\">Congrats! You have successfully installed your app!
+            <br>
+            Checkout <a href=\\"https://probot.github.io/docs/webhooks/\\">Receiving webhooks</a> and <a href=\\"https://probot.github.io/docs/github-api/\\">Interacting with GitHub</a> to learn more!</h1>
+        </div>
+      </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>
+  </body>
+</html>"
+`;
+
+exports[`Setup app > POST /probot/import > 400 when keys are missing 1`] = `"appId and/or pem and/or webhook_secret missing"`;
+
 exports[`Setup app > POST /probot/import > updates .env 1`] = `
 [
   [
diff --git a/test/apps/default.test.ts b/test/apps/default.test.ts
index b3828a8a..c149be09 100644
--- a/test/apps/default.test.ts
+++ b/test/apps/default.test.ts
@@ -46,6 +46,7 @@ describe("default app", () => {
         expect(actual.text).toMatch("Welcome to probot");
         expect(actual.text).toMatch("A framework for building GitHub Apps");
         expect(actual.text).toMatch(/v\d+\.\d+\.\d+/);
+        expect(actual.text).toMatchSnapshot();
       });
 
       it("returns the correct HTML without values", async () => {
@@ -54,6 +55,7 @@ describe("default app", () => {
           .get("/probot")
           .expect(200);
         expect(actual.text).toMatch("Welcome to your Probot App");
+        expect(actual.text).toMatchSnapshot();
       });
     });
   });
diff --git a/test/apps/setup.test.ts b/test/apps/setup.test.ts
index 7154634f..53782911 100644
--- a/test/apps/setup.test.ts
+++ b/test/apps/setup.test.ts
@@ -133,20 +133,26 @@ describe("Setup app", () => {
 
       await server.load(setupAppFactory(undefined, undefined));
 
-      await request(server.expressApp)
+      const setupResponse = await request(server.expressApp)
         .get("/probot/setup")
         .query({ code: "123" })
         .expect(302)
         .expect("Location", "/apps/my-app/installations/new");
 
+      expect(setupResponse.text).toMatchSnapshot();
+
       expect(mocks.createChannel).toHaveBeenCalledTimes(2);
       expect(mocks.updateDotenv.mock.calls).toMatchSnapshot();
     });
   });
 
   describe("GET /probot/import", () => {
-    it("renders import.handlebars", async () => {
-      await request(server.expressApp).get("/probot/import").expect(200);
+    it("renders importView", async () => {
+      const importView = await request(server.expressApp)
+        .get("/probot/import")
+        .expect(200);
+
+      expect(importView.text).toMatchSnapshot();
     });
   });
 
@@ -175,17 +181,23 @@ describe("Setup app", () => {
         webhook_secret: "baz",
       });
 
-      await request(server.expressApp)
+      const importResponse = await request(server.expressApp)
         .post("/probot/import")
         .set("content-type", "application/json")
         .send(body)
         .expect(400);
+
+      expect(importResponse.text).toMatchSnapshot();
     });
   });
 
   describe("GET /probot/success", () => {
     it("returns a 200 response", async () => {
-      await request(server.expressApp).get("/probot/success").expect(200);
+      const successResponse = await request(server.expressApp)
+        .get("/probot/success")
+        .expect(200);
+
+      expect(successResponse.text).toMatchSnapshot();
 
       expect(mocks.createChannel).toHaveBeenCalledTimes(1);
     });
-- 
2.34.1

Can this PR get fast-tracked, so that we can add more PRs please :)?

Copy link
Contributor

@gr2m gr2m left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I say let's gooo

@Uzlopak
Copy link
Collaborator

Uzlopak commented Nov 14, 2023

@AaronDewes
Fix conflicts and merge? :)))

@AaronDewes
Copy link
Member Author

@AaronDewes Fix conflicts and merge? :)))

Sure, I'll do that in about 1h.

@AaronDewes AaronDewes enabled auto-merge (squash) November 14, 2023 08:35
@AaronDewes AaronDewes merged commit be651aa into probot:beta Nov 14, 2023
15 checks passed
@AaronDewes AaronDewes deleted the modern-node branch November 14, 2023 08:36
Copy link

🎉 This PR is included in version 13.0.0-beta.8 🎉

The release is available on:

Your semantic-release bot 📦🚀

@ben-wilson-peak
Copy link

🔥 Legends, thanks for the hard work!

@mattwynne
Copy link

Amazing! 🎉

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Probot isn't compatible with latest typescript
8 participants