From ad0474da4409a78e4b2be5a311948cc875534444 Mon Sep 17 00:00:00 2001 From: Shelley Vohr Date: Thu, 19 Dec 2024 21:13:12 +0100 Subject: [PATCH] feat: add recent PRs to PR Lookup (#69) * feat: add recent PRs to PR Lookup * fix: go to lookup for PR on click --- src/data.js | 60 ++++++++++++++++---- src/routes/pr-lookup.js | 33 +++++++++-- src/static/css/pr-lookup.css | 101 +++++++++++++++++++++++++++------ src/views/pr-lookup.handlebars | 18 +++++- 4 files changed, 181 insertions(+), 31 deletions(-) diff --git a/src/data.js b/src/data.js index c164187..8363707 100644 --- a/src/data.js +++ b/src/data.js @@ -4,6 +4,11 @@ const ExpiryMap = require('expiry-map'); const pMemoize = require('p-memoize'); const semver = require('semver'); +const REPO_DATA = { + owner: 'electron', + repo: 'electron', +}; + let octokit = null; const getOctokit = async () => { if (octokit) return octokit; @@ -12,10 +17,7 @@ const getOctokit = async () => { if (RELEASE_STATUS_GITHUB_APP_CREDS) { const authOpts = await getAuthOptionsForRepo( - { - owner: 'electron', - name: 'electron', - }, + REPO_DATA, appCredentialsFromString(RELEASE_STATUS_GITHUB_APP_CREDS), ); octokit = new Octokit({ @@ -43,6 +45,21 @@ const getReleasesOrUpdate = pMemoize( }, ); +const getDownloadStatsOrUpdate = pMemoize( + async () => { + const response = await fetch('https://electron-sudowoodo.herokuapp.com/release/active'); + return response.json(); + return { + electron: electronJSON, + nightly: nightlyJSON, + }; + }, + { + cache: new ExpiryMap(60 * 1000), + cacheKey: () => 'download_stats', + }, +); + const getActiveReleasesOrUpdate = pMemoize( async () => { const response = await fetch('https://electron-sudowoodo.herokuapp.com/release/active'); @@ -88,6 +105,31 @@ const getGitHubRelease = pMemoize( }, ); +const getRecentPRs = pMemoize( + async () => { + try { + const { data } = await ( + await getOctokit() + ).pulls.list({ + ...REPO_DATA, + state: 'closed', + base: 'main', + }); + return data + .filter((pr) => { + return !pr.user.type !== 'Bot' && pr.merged_at; + }) + .slice(0, 10); + } catch { + return null; + } + }, + { + cache: new ExpiryMap(10 * 60 * 1000), + cacheKey: () => 'recent_prs', + }, +); + const getPR = pMemoize( async (prNumber) => { try { @@ -95,8 +137,7 @@ const getPR = pMemoize( await ( await getOctokit() ).pulls.get({ - owner: 'electron', - repo: 'electron', + ...REPO_DATA, pull_number: prNumber, mediaType: { format: 'html', @@ -119,8 +160,7 @@ const getPRComments = pMemoize( try { return await octo.paginate( octo.issues.listComments.endpoint.merge({ - owner: 'electron', - repo: 'electron', + ...REPO_DATA, issue_number: prNumber, per_page: 100, }), @@ -140,8 +180,7 @@ const compareTagToCommit = pMemoize( const compare = await ( await getOctokit() ).repos.compareCommits({ - owner: 'electron', - repo: 'electron', + ...REPO_DATA, base: tag, head: commitSha, }); @@ -171,6 +210,7 @@ module.exports = { getAllSudowoodoReleasesOrUpdate, getPR, getPRComments, + getRecentPRs, compareTagToCommit, getTSDefs, }; diff --git a/src/routes/pr-lookup.js b/src/routes/pr-lookup.js index a65c92a..454a7b7 100644 --- a/src/routes/pr-lookup.js +++ b/src/routes/pr-lookup.js @@ -1,11 +1,36 @@ const { Router } = require('express'); +const Handlebars = require('handlebars'); +const { getRecentPRs } = require('../data'); +const a = require('../utils/a'); const router = new Router(); -router.get('/', (req, res) => - res.render('pr-lookup', { - title: 'PR Lookup', - css: 'pr-lookup', +Handlebars.registerPartial('recentPR', function (pr) { + const mergedAt = new Date(pr.merged_at).toLocaleString(); + const author = pr.user.login; + return ` + + #${pr.number} + + ${pr.title} + + ${author} + + ${mergedAt} + `; +}); + +router.get( + '/', + a(async (req, res) => { + const recentPRs = await getRecentPRs(); + res.render('pr-lookup', { + recentPRs, + title: 'PR Lookup', + css: 'pr-lookup', + }); }), ); diff --git a/src/static/css/pr-lookup.css b/src/static/css/pr-lookup.css index 9c3bf59..9ef73c8 100644 --- a/src/static/css/pr-lookup.css +++ b/src/static/css/pr-lookup.css @@ -1,29 +1,28 @@ .lookup-input { - display: flex; - justify-content: center; - align-items: center; - gap: 10px; - padding: 20px; + text-align: center; + margin-bottom: 20px; } -#input { - padding: 10px; - border-radius: 5px; +.lookup-input input[type='text'] { + padding: 8px; + font-size: 16px; border: 1px solid #ccc; - flex-grow: 1; + border-radius: 4px; + width: 200px; } -button { - padding: 10px 20px; - border-radius: 5px; +.lookup-input button { + padding: 8px 16px; + font-size: 16px; + color: #fff; + background-color: #3b4a6b; border: none; - background-color: #2b2e3b; - color: white; + border-radius: 4px; cursor: pointer; } -button:hover { - background-color: #0d4a59; +.lookup-input button:hover { + background-color: #2f3b56; } .error { @@ -35,3 +34,73 @@ button:hover { .error:empty { display: none; } + +h1 { + text-align: center; + color: #3b4a6b; +} + +.search-container { + text-align: center; + margin-bottom: 20px; +} + +.search-container input[type='text'] { + padding: 8px; + font-size: 16px; + border: 1px solid #ccc; + border-radius: 4px; + width: 200px; +} + +.search-container button { + padding: 8px 16px; + font-size: 16px; + color: #fff; + background-color: #3b4a6b; + border: none; + border-radius: 4px; + cursor: pointer; +} + +.search-container button:hover { + background-color: #2f3b56; +} + +table { + width: 100%; + border-collapse: collapse; + margin-top: 20px; + background-color: #fff; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); +} + +th, +td { + text-align: left; + padding: 12px; + border-bottom: 1px solid #ddd; +} + +th { + background-color: #f2f4f7; + color: #3b4a6b; + font-weight: bold; +} + +td a { + color: #3b4a6b; + text-decoration: none; +} + +td a:hover { + text-decoration: underline; +} + +tr:hover { + background-color: #f9fafc; +} + +tr:last-child td { + border-bottom: none; +} diff --git a/src/views/pr-lookup.handlebars b/src/views/pr-lookup.handlebars index 3659368..570b29b 100644 --- a/src/views/pr-lookup.handlebars +++ b/src/views/pr-lookup.handlebars @@ -6,6 +6,23 @@

+
+ + + + + + + + + + + {{#each recentPRs}} + {{> recentPR this}} + {{/each}} + +
PR NumberTitleAuthorDate
+