diff --git a/.github/actions/warmup-remotejson-cache/action.yml b/.github/actions/warmup-remotejson-cache/action.yml index 04d039c68f..5686fb2a0c 100644 --- a/.github/actions/warmup-remotejson-cache/action.yml +++ b/.github/actions/warmup-remotejson-cache/action.yml @@ -33,7 +33,7 @@ runs: - name: Run script if: ${{ inputs.restore-only == '' }} shell: bash - run: node src/archives/scripts/warmup-remotejson.js + run: npm run warmup-remotejson - name: Cache .remotejson-cache (save) if: ${{ inputs.restore-only == '' }} diff --git a/.github/workflows/purge-old-workflow-runs.yml b/.github/workflows/purge-old-workflow-runs.yml index 86d911c7b5..07561b6e50 100644 --- a/.github/workflows/purge-old-workflow-runs.yml +++ b/.github/workflows/purge-old-workflow-runs.yml @@ -7,7 +7,7 @@ name: Purge old workflow runs on: workflow_dispatch: schedule: - - cron: '20 */2 * * *' # Run every 2 hours at 20 minutes past the hour + - cron: '20 * * * *' # Run every hour at 20 minutes past the hour permissions: contents: write diff --git a/package.json b/package.json index 3c2a5bde5a..1a97aa8b85 100644 --- a/package.json +++ b/package.json @@ -88,7 +88,7 @@ "update-internal-links": "tsx src/links/scripts/update-internal-links.ts", "validate-asset-images": "tsx src/assets/scripts/validate-asset-images.ts", "validate-github-github-docs-urls": "tsx src/links/scripts/validate-github-github-docs-urls/index.ts", - "warmup-remotejson": "node src/archives/scripts/warmup-remotejson.js" + "warmup-remotejson": "tsx src/archives/scripts/warmup-remotejson.ts" }, "lint-staged": { "*.{js,mjs,ts,tsx}": "eslint --cache --fix", diff --git a/src/archives/lib/is-archived-version.d.ts b/src/archives/lib/is-archived-version.d.ts deleted file mode 100644 index 02696d2351..0000000000 --- a/src/archives/lib/is-archived-version.d.ts +++ /dev/null @@ -1,9 +0,0 @@ -import type { ExtendedRequest } from '@/types' - -type IsArchivedInfo = { - isArchived?: boolean - requestedVersion?: string -} - -export declare function isArchivedVersion(req: ExtendedRequest): IsArchivedInfo -export declare function isArchivedVersionByPath(pathToCheck: string): IsArchivedInfo diff --git a/src/archives/lib/is-archived-version.js b/src/archives/lib/is-archived-version.ts similarity index 59% rename from src/archives/lib/is-archived-version.js rename to src/archives/lib/is-archived-version.ts index c7d145a45d..801905b03d 100644 --- a/src/archives/lib/is-archived-version.js +++ b/src/archives/lib/is-archived-version.ts @@ -1,14 +1,20 @@ -import patterns from '#src/frame/lib/patterns.js' -import { deprecated } from '#src/versions/lib/enterprise-server-releases.js' +import patterns from '@/frame/lib/patterns.js' +import { deprecated } from '@/versions/lib/enterprise-server-releases.js' +import type { ExtendedRequest } from '@/types' -export function isArchivedVersion(req) { +type IsArchivedInfo = { + isArchived?: boolean + requestedVersion?: string +} + +export function isArchivedVersion(req: ExtendedRequest): IsArchivedInfo { // if this is an assets path, use the referrer // if this is a docs path, use the req.path const pathToCheck = patterns.assetPaths.test(req.path) ? req.get('referrer') : req.path return isArchivedVersionByPath(pathToCheck || '') } -export function isArchivedVersionByPath(pathToCheck) { +export function isArchivedVersionByPath(pathToCheck: string): IsArchivedInfo { // ignore paths that don't have an enterprise version number if ( !( @@ -22,10 +28,10 @@ export function isArchivedVersionByPath(pathToCheck) { // extract enterprise version from path, e.g. 2.16 const requestedVersion = pathToCheck.includes('enterprise-server@') ? pathToCheck.match(patterns.getEnterpriseServerNumber)?.[1] - : pathToCheck.match(patterns.getEnterpriseVersionNumber)[1] + : pathToCheck.match(patterns.getEnterpriseVersionNumber)?.[1] // bail if the request version is not deprecated - if (!deprecated.includes(requestedVersion)) { + if (!requestedVersion || !deprecated.includes(requestedVersion)) { return {} } diff --git a/src/archives/lib/old-versions-utils.js b/src/archives/lib/old-versions-utils.ts similarity index 84% rename from src/archives/lib/old-versions-utils.js rename to src/archives/lib/old-versions-utils.ts index 03767995b9..0952c97fba 100644 --- a/src/archives/lib/old-versions-utils.js +++ b/src/archives/lib/old-versions-utils.ts @@ -1,8 +1,8 @@ import path from 'path' -import { supported, latest } from '#src/versions/lib/enterprise-server-releases.js' -import patterns from '#src/frame/lib/patterns.js' -import nonEnterpriseDefaultVersion from '#src/versions/lib/non-enterprise-default-version.js' -import { allVersions } from '#src/versions/lib/all-versions.js' +import { supported, latest } from '@/versions/lib/enterprise-server-releases.js' +import patterns from '@/frame/lib/patterns.js' +import nonEnterpriseDefaultVersion from '@/versions/lib/non-enterprise-default-version.js' +import { allVersions } from '@/versions/lib/all-versions.js' const latestNewVersion = `enterprise-server@${latest}` const oldVersions = ['dotcom'].concat(supported) const newVersions = Object.keys(allVersions) @@ -18,7 +18,7 @@ const newVersions = Object.keys(allVersions) // return an old version like 2.21. // Fall back to latest GHES version if one can't be found, // for example, if the new version is private-instances@latest. -export function getOldVersionFromNewVersion(newVersion) { +export function getOldVersionFromNewVersion(newVersion: string) { return newVersion === nonEnterpriseDefaultVersion ? 'dotcom' : oldVersions.find((oldVersion) => newVersion.includes(oldVersion)) || latest @@ -27,7 +27,7 @@ export function getOldVersionFromNewVersion(newVersion) { // Given an old version like 2.21, // return a new version like enterprise-server@2.21. // Fall back to latest GHES version if one can't be found. -export function getNewVersionFromOldVersion(oldVersion) { +export function getNewVersionFromOldVersion(oldVersion: string) { return oldVersion === 'dotcom' ? nonEnterpriseDefaultVersion : newVersions.find((newVersion) => newVersion.includes(oldVersion)) || latestNewVersion @@ -35,7 +35,7 @@ export function getNewVersionFromOldVersion(oldVersion) { // Given an old path like /enterprise/2.21/user/github/category/article, // return an old version like 2.21. -export function getOldVersionFromOldPath(oldPath) { +export function getOldVersionFromOldPath(oldPath: string) { // We should never be calling this function on a path that starts with a new version, // so we can assume the path either uses the old /enterprise format or it's dotcom. if (!patterns.enterprise.test(oldPath)) return 'dotcom' @@ -46,7 +46,7 @@ export function getOldVersionFromOldPath(oldPath) { // Given an old path like /en/enterprise/2.21/user/github/category/article, // return a new path like /en/enterprise-server@2.21/github/category/article. -export function getNewVersionedPath(oldPath, languageCode = '') { +export function getNewVersionedPath(oldPath: string, languageCode = '') { // It's possible a new version has been injected into an old path // via syntax like: /en/enterprise/{{ currentVersion }}/admin/category/article // which could resolve to /en/enterprise/private-instances@latest/admin/category/article, @@ -58,7 +58,7 @@ export function getNewVersionedPath(oldPath, languageCode = '') { // If no new version was found, assume path contains an old version, like 2.21 if (!newVersion) { - const oldVersion = getOldVersionFromOldPath(oldPath, languageCode) + const oldVersion = getOldVersionFromOldPath(oldPath) newVersion = getNewVersionFromOldVersion(oldVersion) } diff --git a/src/archives/middleware/archived-enterprise-versions-assets.ts b/src/archives/middleware/archived-enterprise-versions-assets.ts index 5d7799777e..3d15ad2f16 100644 --- a/src/archives/middleware/archived-enterprise-versions-assets.ts +++ b/src/archives/middleware/archived-enterprise-versions-assets.ts @@ -2,7 +2,7 @@ import got from 'got' import type { Response, NextFunction } from 'express' import patterns from '@/frame/lib/patterns.js' -import { isArchivedVersion } from '@/archives/lib/is-archived-version.js' +import { isArchivedVersion } from '@/archives/lib/is-archived-version' import { setFastlySurrogateKey, SURROGATE_ENUMS, diff --git a/src/archives/middleware/archived-enterprise-versions.ts b/src/archives/middleware/archived-enterprise-versions.ts index d0fbc2d6d5..3bc0448475 100644 --- a/src/archives/middleware/archived-enterprise-versions.ts +++ b/src/archives/middleware/archived-enterprise-versions.ts @@ -10,7 +10,7 @@ import { } from '@/versions/lib/enterprise-server-releases.js' import patterns from '@/frame/lib/patterns.js' import versionSatisfiesRange from '@/versions/lib/version-satisfies-range.js' -import { isArchivedVersion } from '@/archives/lib/is-archived-version.js' +import { isArchivedVersion } from '@/archives/lib/is-archived-version' import { setFastlySurrogateKey, SURROGATE_ENUMS, diff --git a/src/archives/scripts/warmup-remotejson.js b/src/archives/scripts/warmup-remotejson.ts similarity index 79% rename from src/archives/scripts/warmup-remotejson.js rename to src/archives/scripts/warmup-remotejson.ts index 7a2d431d99..565d809f99 100755 --- a/src/archives/scripts/warmup-remotejson.js +++ b/src/archives/scripts/warmup-remotejson.ts @@ -19,12 +19,11 @@ // [end-readme] import { program } from 'commander' -import semver from 'semver' +import semver, { SemVer } from 'semver' -import getRemoteJSON from '#src/frame/lib/get-remote-json.js' +import getRemoteJSON from '@/frame/lib/get-remote-json.js' import { deprecated, - firstReleaseStoredInBlobStorage, lastVersionWithoutArchivedRedirectsFile, } from '#src/versions/lib/enterprise-server-releases.js' @@ -36,18 +35,14 @@ program main() -function version2url(version) { - const inBlobStorage = semver.gte( - semver.coerce(version).raw, - semver.coerce(firstReleaseStoredInBlobStorage).raw, - ) +function version2url(version: string | SemVer) { return `https://github.github.com/docs-ghes-${version}/redirects.json` } -function withArchivedRedirectsFile(version) { +function withArchivedRedirectsFile(version: string | SemVer) { return semver.eq( - semver.coerce(version).raw, - semver.coerce(lastVersionWithoutArchivedRedirectsFile).raw, + semver.coerce(version)?.raw || '', + semver.coerce(lastVersionWithoutArchivedRedirectsFile)?.raw || '', ) } diff --git a/src/archives/tests/deprecated-enterprise-versions.js b/src/archives/tests/deprecated-enterprise-versions.ts similarity index 85% rename from src/archives/tests/deprecated-enterprise-versions.js rename to src/archives/tests/deprecated-enterprise-versions.ts index 8dc3b7a3d4..d5baff1c41 100644 --- a/src/archives/tests/deprecated-enterprise-versions.js +++ b/src/archives/tests/deprecated-enterprise-versions.ts @@ -1,8 +1,8 @@ import { describe, expect, test, vi } from 'vitest' -import enterpriseServerReleases from '#src/versions/lib/enterprise-server-releases.js' -import { get, getDOM } from '#src/tests/helpers/e2etest.js' -import { SURROGATE_ENUMS } from '#src/frame/middleware/set-fastly-surrogate-key.js' +import enterpriseServerReleases from '@/versions/lib/enterprise-server-releases.js' +import { get, getDOM } from '@/tests/helpers/e2etest-ts' +import { SURROGATE_ENUMS } from '@/frame/middleware/set-fastly-surrogate-key.js' describe('enterprise deprecation', () => { vi.setConfig({ testTimeout: 60 * 1000 }) @@ -45,8 +45,8 @@ describe('enterprise deprecation', () => { test('handles requests for deprecated Enterprise pages ( >=2.13 )', async () => { expect(enterpriseServerReleases.deprecated.includes('2.13')).toBe(true) - const $ = await getDOM('/en/enterprise/2.13/user/articles/about-branches') - expect($.res.statusCode).toBe(200) + const { $, res } = await getDOM('/en/enterprise/2.13/user/articles/about-branches') + expect(res.statusCode).toBe(200) expect($('h1').first().text()).toBe('About branches') }) @@ -60,27 +60,27 @@ describe('enterprise deprecation', () => { test('handles requests for deprecated Enterprise pages ( <2.13 )', async () => { expect(enterpriseServerReleases.deprecated.includes('2.12')).toBe(true) - const $ = await getDOM('/enterprise/2.12/user/articles/about-branches') - expect($.res.statusCode).toBe(200) + const { $, res } = await getDOM('/enterprise/2.12/user/articles/about-branches') + expect(res.statusCode).toBe(200) expect($('h2').text()).toBe('About branches') }) test('handles requests for deprecated Enterprise version 11.10.340', async () => { expect(enterpriseServerReleases.deprecated.includes('11.10.340')).toBe(true) - const $ = await getDOM('/enterprise/11.10.340/admin/articles/adding-teams') - expect($.res.statusCode).toBe(200) + const { $, res } = await getDOM('/enterprise/11.10.340/admin/articles/adding-teams') + expect(res.statusCode).toBe(200) expect($('h2').text()).toBe('Adding teams') }) test('has working admin guide links ( <2.13 )', async () => { const guidesPath = '/enterprise/2.12/admin' - let $ = await getDOM(`${guidesPath}/guides`) - const firstLink = $('[class="guide-section"]').children('a').attr('href') + const { $: $1 } = await getDOM(`${guidesPath}/guides`) + const firstLink = $1('[class="guide-section"]').children('a').attr('href') - $ = await getDOM(`${guidesPath}/${firstLink}`) - expect($.res.statusCode).toBe(200) + const { $: $2, res } = await getDOM(`${guidesPath}/${firstLink}`) + expect(res.statusCode).toBe(200) // this test assumes the Installation guide is the first link on the guides page - expect($('h2').text()).toBe('Installing and configuring GitHub Enterprise') + expect($2('h2').text()).toBe('Installing and configuring GitHub Enterprise') }) }) @@ -139,22 +139,22 @@ describe('recently deprecated redirects', () => { describe('deprecation banner', () => { test('renders a deprecation warning banner on oldest supported Enterprise version', async () => { - const $ = await getDOM(`/en/enterprise/${enterpriseServerReleases.oldestSupported}`) + const { $ } = await getDOM(`/en/enterprise/${enterpriseServerReleases.oldestSupported}`) expect($('[data-testid=deprecation-banner]').length).toBe(1) }) test('does not render a deprecation warning banner on other Enterprise versions', async () => { - const $ = await getDOM(`/en/enterprise/${enterpriseServerReleases.latest}`) + const { $ } = await getDOM(`/en/enterprise/${enterpriseServerReleases.latest}`) expect($('[data-testid=deprecation-banner]').length).toBe(0) }) test('deprecation warning banner includes a date', async () => { - const $ = await getDOM(`/en/enterprise/${enterpriseServerReleases.oldestSupported}`) + const { $ } = await getDOM(`/en/enterprise/${enterpriseServerReleases.oldestSupported}`) expect($('[data-testid=deprecation-banner] b').text().endsWith('discontinued on .')).toBe(false) }) test('deprecation warning banner includes the right text depending on the date', async () => { - const $ = await getDOM(`/en/enterprise/${enterpriseServerReleases.oldestSupported}`) + const { $ } = await getDOM(`/en/enterprise/${enterpriseServerReleases.oldestSupported}`) const expectedString = enterpriseServerReleases.isOldestReleaseDeprecated ? 'was discontinued' : 'will be discontinued' @@ -164,24 +164,28 @@ describe('deprecation banner', () => { describe('does not render survey prompt or contribution button', () => { test('does not render survey prompt', async () => { - let $ = await getDOM(`/en/enterprise/${enterpriseServerReleases.latest}/github`) - expect($('[data-testid="survey-form"]').length).toBeGreaterThan(0) - $ = await getDOM(`/en/enterprise/${enterpriseServerReleases.oldestSupported}/github`) + const { $: $1 } = await getDOM(`/en/enterprise/${enterpriseServerReleases.latest}/github`) + expect($1('[data-testid="survey-form"]').length).toBeGreaterThan(0) + const { $: $2 } = await getDOM( + `/en/enterprise/${enterpriseServerReleases.oldestSupported}/github`, + ) if (enterpriseServerReleases.isOldestReleaseDeprecated) { - expect($('[data-testid="survey-form"]').length).toBe(0) + expect($2('[data-testid="survey-form"]').length).toBe(0) } else { - expect($('[data-testid="survey-form"]').length).toBeGreaterThan(0) + expect($2('[data-testid="survey-form"]').length).toBeGreaterThan(0) } }) test('does not render contribution button', async () => { - let $ = await getDOM(`/en/enterprise/${enterpriseServerReleases.latest}/github`) - expect($('.contribution').length).toBeGreaterThan(0) - $ = await getDOM(`/en/enterprise/${enterpriseServerReleases.oldestSupported}/github`) + const { $: $1 } = await getDOM(`/en/enterprise/${enterpriseServerReleases.latest}/github`) + expect($1('.contribution').length).toBeGreaterThan(0) + const { $: $2 } = await getDOM( + `/en/enterprise/${enterpriseServerReleases.oldestSupported}/github`, + ) if (enterpriseServerReleases.isOldestReleaseDeprecated) { - expect($('.contribution').length).toBe(0) + expect($2('.contribution').length).toBe(0) } else { - expect($('[data-testid=survey-form]').length).toBeGreaterThan(0) + expect($2('[data-testid=survey-form]').length).toBeGreaterThan(0) } }) }) diff --git a/src/content-render/unified/rewrite-local-links.js b/src/content-render/unified/rewrite-local-links.js index 8f5d2d87e2..99116307ae 100644 --- a/src/content-render/unified/rewrite-local-links.js +++ b/src/content-render/unified/rewrite-local-links.js @@ -5,7 +5,7 @@ import stripAnsi from 'strip-ansi' import { visit } from 'unist-util-visit' import { distance } from 'fastest-levenshtein' import { getPathWithoutLanguage, getVersionStringFromPath } from '#src/frame/lib/path-utils.js' -import { getNewVersionedPath } from '#src/archives/lib/old-versions-utils.js' +import { getNewVersionedPath } from '#src/archives/lib/old-versions-utils.ts' import patterns from '#src/frame/lib/patterns.js' import { deprecated, latest } from '#src/versions/lib/enterprise-server-releases.js' import nonEnterpriseDefaultVersion from '#src/versions/lib/non-enterprise-default-version.js' diff --git a/src/frame/middleware/helmet.ts b/src/frame/middleware/helmet.ts index d5e3e84875..16ba54a2d9 100644 --- a/src/frame/middleware/helmet.ts +++ b/src/frame/middleware/helmet.ts @@ -1,6 +1,6 @@ import type { NextFunction, Request, Response } from 'express' import helmet from 'helmet' -import { isArchivedVersion } from '@/archives/lib/is-archived-version.js' +import { isArchivedVersion } from '@/archives/lib/is-archived-version' import versionSatisfiesRange from '@/versions/lib/version-satisfies-range.js' import { languagePrefixPathRegex } from '@/languages/lib/languages.js' diff --git a/src/pageinfo/middleware.ts b/src/pageinfo/middleware.ts index d321717534..cda18f015a 100644 --- a/src/pageinfo/middleware.ts +++ b/src/pageinfo/middleware.ts @@ -14,7 +14,7 @@ import shortVersions from '@/versions/middleware/short-versions.js' import contextualize from '@/frame/middleware/context/context' import features from '@/versions/middleware/features.js' import getRedirect from '@/redirects/lib/get-redirect.js' -import { isArchivedVersionByPath } from '@/archives/lib/is-archived-version.js' +import { isArchivedVersionByPath } from '@/archives/lib/is-archived-version' import { readCompressedJsonFile } from '@/frame/lib/read-json-file.js' const router = express.Router() diff --git a/src/search/tests/rendering.ts b/src/search/tests/rendering.ts index 660f9d553a..61697806f2 100644 --- a/src/search/tests/rendering.ts +++ b/src/search/tests/rendering.ts @@ -32,7 +32,7 @@ describeIfElasticsearchURL('search rendering page', () => { // To see why this will work, // see src/search/tests/fixtures/search-indexes/github-docs-dotcom-en-records.json // which clearly has a record with the title "Foo" - const $ = await getDOM('/en/search?query=foo') + const { $ } = await getDOM('/en/search?query=foo') expect($('h1').text()).toMatch(/\d+ Search results for "foo"/) // Note it testid being 'search-result', not 'search-results' @@ -62,7 +62,7 @@ describeIfElasticsearchURL('search rendering page', () => { }) test('debug search', async () => { - const $ = await getDOM('/en/search?query=foo&debug=1') + const { $ } = await getDOM('/en/search?query=foo&debug=1') expect($('h1').text()).toMatch(/\d+ Search results for "foo"/) // Note it testid being 'search-result', not 'search-results' @@ -74,21 +74,21 @@ describeIfElasticsearchURL('search rendering page', () => { }) test('no query', async () => { - const $ = await getDOM('/en/search') + const { $ } = await getDOM('/en/search') const results = $('[data-testid="search-result"]') expect(results.length).toBe(0) expect($('[data-testid="search-results"]').text()).toMatch('Enter a search term') }) test('empty query', async () => { - const $ = await getDOM('/en/search?query=') + const { $ } = await getDOM('/en/search?query=') const results = $('[data-testid="search-result"]') expect(results.length).toBe(0) expect($('[data-testid="search-results"]').text()).toMatch('Enter a search term') }) test('find nothing', async () => { - const $ = await getDOM('/en/search?query=xojixjoiwejhfoiuwehjfioweufhj') + const { $ } = await getDOM('/en/search?query=xojixjoiwejhfoiuwehjfioweufhj') const results = $('[data-testid="search-result"]') expect(results.length).toBe(0) expect($('[data-testid="search-results"]').text()).toMatch('0 Search results') @@ -100,7 +100,7 @@ describeIfElasticsearchURL('search rendering page', () => { }) test('links per version in pathname', async () => { - const $ = await getDOM('/en/enterprise-cloud@latest/search?query=foo') + const { $ } = await getDOM('/en/enterprise-cloud@latest/search?query=foo') expect($('[data-testid="search-results"]').text()).toMatch('Exclusively for GHEC') // Note it testid being 'search-result', not 'search-results' const results = $('[data-testid="search-result"]') @@ -115,14 +115,14 @@ describeIfElasticsearchURL('search rendering page', () => { }) test('invalid parameters (page)', async () => { - const $ = await getDOM('/en/search?query=foo&page=999') + const { $ } = await getDOM('/en/search?query=foo&page=999') expect($('[data-testid="search-results"]').text()).toMatch('Not a valid value (999)') const results = $('[data-testid="search-result"]') expect(results.length).toBe(0) }) test('invalid parameters (size)', async () => { - const $ = await getDOM('/en/search?query=foo&size=888') + const { $ } = await getDOM('/en/search?query=foo&size=888') expect($('[data-testid="search-results"]').text()).toMatch('Not a valid value (888)') const results = $('[data-testid="search-result"]') expect(results.length).toBe(0) @@ -134,14 +134,14 @@ describeIfElasticsearchURL('search rendering page', () => { }) test('more than one search query', async () => { - const $ = await getDOM('/en/search?query=foo&query=bar') + const { $ } = await getDOM('/en/search?query=foo&query=bar') expect($('[data-testid="search-results"]').text()).toMatch('Cannot have multiple values') const results = $('[data-testid="search-result"]') expect(results.length).toBe(0) }) test("search with 'toplevel' query string", async () => { - const $ = await getDOM('/en/search?query=foo&toplevel=Baring') + const { $ } = await getDOM('/en/search?query=foo&toplevel=Baring') expect($('h1').text()).toMatch(/\d+ Search results for "foo"/) // Note it testid being 'search-result', not 'search-results' diff --git a/src/search/tests/search.ts b/src/search/tests/search.ts index 5fed615cd5..6ee506ad73 100644 --- a/src/search/tests/search.ts +++ b/src/search/tests/search.ts @@ -5,7 +5,7 @@ describe('search results page', () => { vi.setConfig({ testTimeout: 60 * 1000 }) test('says something if no query is provided', async (): Promise => { - const $ = await getDOM('/en/search') + const { $ } = await getDOM('/en/search') const $container = $('[data-testid="search-results"]') expect($container.text()).toMatch(/Enter a search term/) // Default is the frontmatter title of the content/search/index.md @@ -14,7 +14,7 @@ describe('search results page', () => { test('says something if query is empty', async (): Promise => { const queryParams = new URLSearchParams({ query: ' ' }).toString() - const $ = await getDOM(`/en/search?${queryParams}`) + const { $ } = await getDOM(`/en/search?${queryParams}`) const $container = $('[data-testid="search-results"]') expect($container.text()).toMatch(/Enter a search term/) }) @@ -22,7 +22,7 @@ describe('search results page', () => { test('mentions search term in h1', async (): Promise => { const searchTerm = 'peterbe' const queryParams = new URLSearchParams({ query: searchTerm }).toString() - const $ = await getDOM(`/en/search?${queryParams}`) + const { $ } = await getDOM(`/en/search?${queryParams}`) const $container = $('[data-testid="search-results"]') const h1Text: string = $container.find('h1').text() diff --git a/src/tests/helpers/e2etest-ts.ts b/src/tests/helpers/e2etest-ts.ts index 9d489cdf28..16b5dadde3 100644 --- a/src/tests/helpers/e2etest-ts.ts +++ b/src/tests/helpers/e2etest-ts.ts @@ -122,8 +122,8 @@ export async function getDOMCached( ): Promise { const key = `${route}::${JSON.stringify(options)}` if (!getDOMCache.has(key)) { - const dom = await getDOM(route, options) - getDOMCache.set(key, dom) + const { $ } = await getDOM(route, options) + getDOMCache.set(key, $) } // The non-null assertion is safe here because we've just set the key if it didn't exist return getDOMCache.get(key)! @@ -136,7 +136,10 @@ export async function getDOMCached( * @param options - Options for fetching the DOM. * @returns A promise that resolves to the loaded DOM object. */ -export async function getDOM(route: string, options: GetDOMOptions = {}): Promise { +export async function getDOM( + route: string, + options: GetDOMOptions = {}, +): Promise<{ $: cheerio.Root; res: Response }> { const { headers, allow500s = false, allow404 = false, retries = 0 } = options const res = await get(route, { followRedirects: true, headers, retries }) @@ -150,10 +153,7 @@ export async function getDOM(route: string, options: GetDOMOptions = {}): Promis const $ = cheerio.load(res.body || '', { xmlMode: true }) - // Extend the Cheerio instance with the response object - ;($ as any).res = { ...res } - - return $ + return { $, res } } /** diff --git a/src/workflows/purge-old-workflow-runs.js b/src/workflows/purge-old-workflow-runs.js index 973a96b27b..51cd86d897 100755 --- a/src/workflows/purge-old-workflow-runs.js +++ b/src/workflows/purge-old-workflow-runs.js @@ -18,7 +18,7 @@ * For every run found, it deletes its logs and its run. * * The total number of deletions is limited by the `MAX_DELETIONS` - * environment variable. The default is 100. + * environment variable. The default is 2000. * */ import fs from 'fs' @@ -29,7 +29,7 @@ import { getOctokit } from '@actions/github' main() async function main() { const DRY_RUN = Boolean(JSON.parse(process.env.DRY_RUN || 'false')) - const MAX_DELETIONS = parseInt(JSON.parse(process.env.MAX_DELETIONS || '100')) + const MAX_DELETIONS = parseInt(JSON.parse(process.env.MAX_DELETIONS || '2000')) const MIN_AGE_DAYS = parseInt(process.env.MIN_AGE_DAYS || '90', 10) const [owner, repo] = process.env.GITHUB_REPOSITORY.split('/') @@ -134,11 +134,11 @@ async function deleteWorkflowRuns( owner, repo, workflow, - { dryRun = false, minAgeDays = 100, maxDeletions = 1000 }, + { dryRun = false, minAgeDays = 90, maxDeletions = 2000 }, ) { // https://docs.github.com/en/search-github/getting-started-with-searching-on-github/understanding-the-search-syntax#query-for-dates const minCreated = new Date(Date.now() - minAgeDays * 24 * 60 * 60 * 1000) - const minCreatedSearch = `<=${minCreated.toISOString().split('T')[0]}` + const minCreatedSearch = `<${minCreated.toISOString().split('T')[0]}` // Delete is 10, but max is 100. But if we're only going to delete, // 30, use 30. And if we're only going to delete 5, use the default // per_page value of 10.