1
0
mirror of synced 2025-12-19 09:57:42 -05:00

Port whats-new-changelog.js to TypeScript (#51199)

This commit is contained in:
Peter Bengtsson
2024-06-14 13:35:29 -04:00
committed by GitHub
parent d0b8171c47
commit 6ca0794957
5 changed files with 56 additions and 20 deletions

View File

@@ -4,11 +4,13 @@ import path from 'path'
import Parser from 'rss-parser'
import type { ChangelogItem } from '@/types'
const CHANGELOG_CACHE_FILE_PATH = process.env.CHANGELOG_CACHE_FILE_PATH
// This is useful to set when doing things like sync search.
const CHANGELOG_DISABLED = Boolean(JSON.parse(process.env.CHANGELOG_DISABLED || 'false'))
async function getRssFeed(url) {
async function getRssFeed(url: string) {
const parser = new Parser({ timeout: 5000 })
const feedUrl = `${url}/feed`
let feed
@@ -16,14 +18,18 @@ async function getRssFeed(url) {
try {
feed = await parser.parseURL(feedUrl)
} catch (err) {
console.error(`cannot get ${feedUrl}: ${err.message}`)
console.error(`cannot get ${feedUrl}: ${err instanceof Error ? err.message : err}`)
return
}
return feed
}
export async function getChangelogItems(prefix, feedUrl, ignoreCache = false) {
export async function getChangelogItems(
prefix: string | undefined,
feedUrl: string,
ignoreCache = false,
): Promise<ChangelogItem[] | undefined> {
if (CHANGELOG_DISABLED) {
if (process.env.NODE_ENV === 'development') {
console.warn(`Downloading changelog (${feedUrl}) items is disabled.`)
@@ -44,14 +50,15 @@ export async function getChangelogItems(prefix, feedUrl, ignoreCache = false) {
}
// only show the first 3 posts
const changelog = feed.items.slice(0, 3).map((item) => {
const changelog: ChangelogItem[] = feed.items.slice(0, 3).map((item) => {
const rawTitle = item.title as string
// remove the prefix if it exists (Ex: 'GitHub Actions: '), where the colon and expected whitespace should be hardcoded.
const title = prefix ? item.title.replace(new RegExp(`^${prefix}`), '') : item.title
const title = prefix ? rawTitle.replace(new RegExp(`^${prefix}`), '') : rawTitle
return {
// capitalize the first letter of the title
title: title.trim().charAt(0).toUpperCase() + title.slice(1),
date: item.isoDate,
href: item.link,
date: item.isoDate as string,
href: item.link as string,
}
})
@@ -65,13 +72,13 @@ export async function getChangelogItems(prefix, feedUrl, ignoreCache = false) {
const globalCache = new Map()
function getChangelogCacheKey(prefix, feedUrl) {
function getChangelogCacheKey(prefix: string | undefined, feedUrl: string) {
// Return a string that is only letters so it's safe to use this
// for the filename when caching to disk.
return `${prefix || ''}${feedUrl}`.replace(/[^a-z]+/gi, '')
}
function getDiskCachePath(prefix, feedUrl) {
function getDiskCachePath(prefix: string | undefined, feedUrl: string) {
// When in local development or in tests, use disk caching
if (process.env.NODE_ENV === 'test' || process.env.NODE_ENV === 'development') {
if (CHANGELOG_CACHE_FILE_PATH) {
@@ -84,7 +91,7 @@ function getDiskCachePath(prefix, feedUrl) {
}
}
function getChangelogItemsFromCache(prefix, feedUrl) {
function getChangelogItemsFromCache(prefix: string | undefined, feedUrl: string) {
const cacheKey = getChangelogCacheKey(prefix, feedUrl)
if (globalCache.get(cacheKey)) {
@@ -103,7 +110,7 @@ function getChangelogItemsFromCache(prefix, feedUrl) {
return payload
} catch (err) {
// If it wasn't on disk, that's fine.
if (err.code === 'ENOENT') return
if (err instanceof Error && 'code' in err && err.code === 'ENOENT') return
// The JSON.parse() most likely failed. Ignore the error
// but delete the file so it won't be attempted again.
if (err instanceof SyntaxError) {
@@ -115,7 +122,11 @@ function getChangelogItemsFromCache(prefix, feedUrl) {
}
}
function setChangelogItemsCache(prefix, feedUrl, payload) {
function setChangelogItemsCache(
prefix: string | undefined,
feedUrl: string,
payload: ChangelogItem[],
) {
const cacheKey = getChangelogCacheKey(prefix, feedUrl)
globalCache.set(cacheKey, payload)

View File

@@ -4,10 +4,11 @@ import path from 'path'
import nock from 'nock'
import { afterAll, beforeAll, describe, expect, test } from 'vitest'
import { getChangelogItems } from '#src/changelogs/lib/changelog.js'
import { getChangelogItems } from '@/changelogs/lib/changelog'
import type { ChangelogItem } from '@/types'
describe('getChangelogItems module', () => {
let changelog
let changelog: ChangelogItem[] | undefined
beforeAll(async () => {
const rssFeedContent = await fs.readFile(
@@ -35,7 +36,7 @@ describe('getChangelogItems module', () => {
afterAll(() => nock.cleanAll())
test('changelog contains 3 items', async () => {
expect(changelog.length).toEqual(3)
expect(changelog && changelog.length).toEqual(3)
})
test('each changelog item has expected title, date, and href', async () => {
@@ -57,6 +58,7 @@ describe('getChangelogItems module', () => {
},
]
if (!changelog) throw new Error('changelog is undefined')
for (let i = 0; i < 3; i++) {
const changeLogEntry = changelog[i]
const expectedEntry = expectedChangelogValues[i]

View File

@@ -1,7 +1,15 @@
import { getChangelogItems } from '#src/changelogs/lib/changelog.js'
import getApplicableVersions from '#src/versions/lib/get-applicable-versions.js'
import type { Response, NextFunction } from 'express'
export default async function whatsNewChangelog(req, res, next) {
import { getChangelogItems } from '@/changelogs/lib/changelog.js'
import getApplicableVersions from '@/versions/lib/get-applicable-versions.js'
import type { ExtendedRequest } from '@/types'
export default async function whatsNewChangelog(
req: ExtendedRequest,
res: Response,
next: NextFunction,
) {
if (!req.context) throw new Error('request not contextualized')
if (!req.context.page) return next()
if (!req.context.page.changelog) return next()
const label = req.context.page.changelog.label.split(/\s+/g).join('')
@@ -16,7 +24,7 @@ export default async function whatsNewChangelog(req, res, next) {
}
}
const labelUrls = {
const labelUrls: Record<string, string> = {
education: 'https://github.blog/category/community/education',
enterprise: 'https://github.blog/category/enterprise/',
}

View File

@@ -38,7 +38,7 @@ import categoriesForSupport from './categories-for-support'
import triggerError from '@/observability/middleware/trigger-error'
import secretScanning from '@/secret-scanning/middleware/secret-scanning'
import ghesReleaseNotes from '@/release-notes/middleware/ghes-release-notes'
import whatsNewChangelog from './context/whats-new-changelog.js'
import whatsNewChangelog from './context/whats-new-changelog'
import layout from './context/layout.js'
import currentProductTree from './context/current-product-tree.js'
import genericToc from './context/generic-toc.js'

View File

@@ -74,6 +74,8 @@ export type Context = {
languages?: Languages
redirectNotFound?: string
earlyAccessPageLinks?: string
changelogUrl?: string
whatsNewChangelog?: ChangelogItem[]
secretScanningData?: SecretScanningData[]
ghesReleases?: GHESRelease[]
ghesReleaseNotes?: GHESReleasePatch[]
@@ -123,6 +125,12 @@ export type ReleaseNotes = {
}
}
export type ChangelogItem = {
title: string
date: string
href: string
}
export type SecretScanningData = {
provider: string
supportedSecret: string
@@ -176,6 +184,13 @@ export type Page = {
markdown: string
versions: FrontmatterVersions
applicableVersions: string[]
changelog?: ChangeLog
}
type ChangeLog = {
label: string
prefix?: string
versions?: FrontmatterVersions
}
export type Tree = {