diff --git a/packages/app/lib/probot/CustomProbot.js b/packages/app/lib/probot/CustomProbot.js index e572433e..9b5c98f9 100644 --- a/packages/app/lib/probot/CustomProbot.js +++ b/packages/app/lib/probot/CustomProbot.js @@ -3,39 +3,60 @@ const { Server, Probot } = require('probot'); const { getLog } = require('probot/lib/helpers/get-log'); const { setupAppFactory } = require('./apps/setup'); +const { isProduction } = require('probot/lib/helpers/is-production'); + const { ManifestCreation } = require('probot/lib/manifest-creation'); +const { readEnvOptions } = require('probot/lib/bin/read-env-options'); + const { getErrorHandler } = require('probot/lib/helpers/get-error-handler'); -const { getPrivateKey } = require('@probot/get-private-key'); -async function run(appFn) { +async function run(appFn, additionalOptions) { + + const { - const appId = parseInt(process.env.APP_ID, 10); - const host = process.env.HOST; - const port = parseInt(process.env.PORT || '3000', 10); - const privateKey = getPrivateKey() || undefined; + // log options + logLevel: level, logFormat, logLevelInString, logMessageKey, sentryDsn, - const log = getLog(); + // server options + host, port, webhookPath, webhookProxy, + + // probot options + appId, privateKey, redisConfig, secret, baseUrl + + } = readEnvOptions(additionalOptions?.env); + + const logOptions = { + level, + logFormat, + logLevelInString, + logMessageKey, + sentryDsn + }; + + const log = getLog(logOptions); const serverOptions = { host, - log: log.child({ name: 'server' }), port, - webhookPath: process.env.WEBHOOK_PATH, - webhookProxy: process.env.WEBHOOK_PROXY_URL + webhookPath, + webhookProxy, + log: log.child({ name: 'server' }) }; let probotOptions = { appId, - log: log.child({ name: 'probot' }), privateKey, - secret: process.env.WEBHOOK_SECRET + redisConfig, + secret, + baseUrl, + log: log.child({ name: 'probot' }) }; // use probots own setup app if the probot app // is not configured yet - if (!isProduction() && !isSetup()) { + if (!isProduction() && !isSetup(probotOptions)) { // Workaround for setup (probot/probot#1512) // When probot is started for the first time, it gets into a setup mode @@ -72,22 +93,20 @@ async function run(appFn) { return server; } -function isSetup() { - const appId = parseInt(process.env.APP_ID, 10); - const privateKey = getPrivateKey() || undefined; +function isSetup(options) { + const { + appId, + privateKey + } = options || readEnvOptions(process.env); return !!(appId && privateKey); } -function isProduction() { - return process.env.NODE_ENV === 'production'; -} - function validateSetup() { const setup = new ManifestCreation(); - const manifest = JSON.parse(setup.getManifest(setup.pkg)); + const manifest = JSON.parse(setup.getManifest(setup.pkg, process.env.BASE_URL)); return [ !manifest.url && new Error('No configured in app.yml'), diff --git a/packages/app/lib/probot/apps/setup.js b/packages/app/lib/probot/apps/setup.js index 27ce3b69..86c1012e 100644 --- a/packages/app/lib/probot/apps/setup.js +++ b/packages/app/lib/probot/apps/setup.js @@ -9,6 +9,8 @@ const { getLog } = require('probot/lib/helpers/get-log'); const { randomString } = require('../../util'); +const { importView } = require('probot/lib/views/import'); +const { setupView } = require('probot/lib/views/setup'); function setupAppFactory(host, port) { @@ -36,48 +38,110 @@ function setupAppFactory(host, port) { route.use(getLoggingMiddleware(app.log)); route.get('/probot', async (req, res) => { - const baseUrl = getBaseUrl(req); + const baseUrl = process.env.BASE_URL || getBaseUrl(req); const pkg = setup.pkg; const manifest = setup.getManifest(pkg, baseUrl); const createAppUrl = setup.createAppUrl; + await setup.updateEnv({ + BASE_URL: baseUrl + }); + // Pass the manifest to be POST'd - res.render('setup.hbs', { pkg, createAppUrl, manifest }); + res.writeHead(200, { 'content-type': 'text/html' }).end(setupView({ + name: pkg.name || 'Wuffle', + version: pkg.version, + description: pkg.description, + createAppUrl, + manifest + })); }); route.get('/probot/setup', async (req, res) => { const { code } = req.query; - const app_url = await setup.createAppFromCode(code); - log.info('Setup completed, please start the app'); + if (!code || typeof code !== 'string' || code.length === 0) { + return res + .writeHead(400, { 'content-type': 'text/plain' }) + .end('code missing or invalid'); + } + + const response = await setup.createAppFromCode(code, { + request: app.state.request + }); - res.send(renderSuccess(app_url + '/installations/new')); + const appUrl = app.state.appUrl = `${response}/installations/new`; + + log.warn('Setup completed, please restart the app'); + + log.info(`Visit ${appUrl} to connect GitHub repositories`); + + const location = '/probot/success'; + + return res + .writeHead(302, { + 'content-type': 'text/plain', + location + }) + .end(`Redirecting to ${ location }`); }); route.get('/probot/import', async (_req, res) => { + + const pkg = setup.pkg; 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 }); + + return res + .writeHead(200, { + 'content-type': 'text/html' + }) + .end(importView({ + name: pkg.name || 'Wuffle', + 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; + return res + .writeHead(400, { + 'content-type': 'text/plain' + }) + .end('appId and/or pem and/or webhook_secret missing'); } + await updateDotenv({ APP_ID: appId, PRIVATE_KEY: `"${pem}"`, WEBHOOK_SECRET: webhook_secret }); - log.info('Setup completed, please start the app'); + log.warn('Setup completed, please restart the app.'); - res.send(renderSuccess()); + return res.redirect('/probot/success'); }); - route.get('/', (req, res, next) => res.redirect('/probot')); + route.get('/probot/success', (_req, res) => { + const pkg = setup.pkg; + + const appUrl = app.state.appUrl; + + return res + .writeHead(200, { 'content-type': 'text/html' }) + .end(successView({ + name: pkg.name || 'Wuffle', + appUrl + })); + }); + + route.get('/', (_req, res) => { + return res + .writeHead(302, { 'content-type': 'text/plain', location: '/probot' }) + .end(); + }); }; } @@ -94,40 +158,40 @@ function getBaseUrl(req) { return baseUrl; } - -function renderSuccess(appUrl = null) { +function successView({ name, appUrl }) { return ` - - - - - - - Setup Wuffle App | built with Probot - - - - -
- Probot Logo -
-
-

Congrats! You have successfully installed your app!

- -

- You can now ${ appUrl ? `` : ''} - connect GitHub repositories - ${appUrl ? '' : ''} to your board. -

- -

- Please restart the server to complete the setup. -

+ + + + + + + ${ name } Setup complete + + + + +
+ Probot Logo +
+
+

You completed your ${name} setup!

+ +

+ Go ahead and ${ appUrl ? `` : ''} + connect GitHub repositories + ${appUrl ? '' : ''} to your board. +

+ +

+ Please restart the server to complete the setup. +

+ +
-
- -`; - + + + `; } \ No newline at end of file