From 5de57dd78ee4d0d2f5b5ecf05edeb260b8350b75 Mon Sep 17 00:00:00 2001 From: Kevin Heis Date: Fri, 6 Feb 2026 10:42:12 -0800 Subject: [PATCH 1/2] Update copilot instructions (#59550) --- .github/instructions/all.instructions.md | 6 +++--- .github/instructions/code.instructions.md | 12 ++++++------ 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/.github/instructions/all.instructions.md b/.github/instructions/all.instructions.md index aba2b7f9663f..b370dc3dbb98 100644 --- a/.github/instructions/all.instructions.md +++ b/.github/instructions/all.instructions.md @@ -12,9 +12,9 @@ When you create a pull request: 1. **Always** make the first line of the PR description the following (in italics): - `_Copilot Chat generated this pull request._` + `_GitHub Copilot generated this pull request._` -2. Optionally, you may include a collapsed section summarizing the prompt or discussion with Copilot Chat: +2. Optionally, you may include a collapsed section summarizing the prompt or discussion with Copilot: ```markdown
Prompt summary - submitted by @GITHUB-USER-ID @@ -29,4 +29,4 @@ When you create a pull request: 3. Label with "llm-generated". 4. If an issue exists, include "fixes owner/repo#issue" or "towards owner/repo#issue" as appropriate. 5. Always create PRs in **draft mode** using `--draft` flag. -6. Always _escape backticks_ when you use gh cli. +6. When you are using gh cli, always _escape backticks_. diff --git a/.github/instructions/code.instructions.md b/.github/instructions/code.instructions.md index 24edc8a25681..f2ba0e5bba17 100644 --- a/.github/instructions/code.instructions.md +++ b/.github/instructions/code.instructions.md @@ -9,10 +9,10 @@ For code reviews, follow guidelines, tests, and validate instructions. For creat ## Guidelines - If available, use ripgrep (`rg`) instead of `grep`. -- Make sure to always _escape backticks_ when using gh cli. +- When using gh cli, always _escape backticks_. - All scripts should be listed in `package.json` and use `tsx`. -- Whenever you create or comment on an issue or pull request, indicate you are an LLM. -- Be careful fetching full HTML pages off the internet. Prefer to use gh cli whenever possible for github.com. Limit the number of tokens when grabbing HTML. +- Whenever you create or comment on an issue or pull request, indicate you are GitHub Copilot. +- Be careful fetching full HTML pages off the internet. Prefer to use MCP or gh cli whenever possible for github.com. Limit the number of tokens when grabbing HTML. - Avoid pull requests with over 300 lines of code changed. When significantly larger, offer to split up into smaller pull requests if possible. - All new code should be written in TypeScript and not JavaScript. - We use absolute imports, relative to the `src` directory, using the `@` symbol. For example, `getRedirect` which lives in `src/redirects/lib/get-redirect.ts` can be imported with `import getRedirect from '@/redirects/lib/get-redirect'`. The same rule applies for TypeScript (`.ts`) imports, e.g. `import type { GeneralSearchHit } from '@/search/types'` @@ -43,7 +43,7 @@ Run the following commands to validate your changes: 0. Ask the human if they would like you to follow these steps. 1. If this is new work, make sure you have the latest changes by running `git checkout main && git pull`. If this is existing work, update the branch you are working on with the head branch -- usually `main`. -2. If the human provides a GitHub issue, use gh cli to read the issue and all comments. +2. If the human provides a GitHub issue, use MCP or gh cli to read the issue and all comments. 3. Begin by evaluating impact, effort, and estimate non-test lines of code that will change. Ask for more context and examples if needed. 4. If you are running in agentic mode, _stop_ at this point and request approval from the human. 5. If you need to add or change tests, work on tests before implementing. @@ -52,7 +52,7 @@ Run the following commands to validate your changes: 8. Validate that any new or changed tests pass. See "Tests". 9. Validate that these changes meet our guidelines. See "Guidelines". 10. If you are running in agentic mode, _stop_ at this point and request review before continuing. Suggest how the human should review the changes. -11. If a branch and pull request already exist, commit and push, then _concisely_ comment on the pull request that you are an LLM and what changes you made and why. +11. If a branch and pull request already exist, commit and push, then _concisely_ comment on the pull request that you are GitHub Copilot and what changes you made and why. 12. If this is new work and no pull request exists yet, make a pull request: - label "llm-generated" - draft mode @@ -61,4 +61,4 @@ Run the following commands to validate your changes: 14. If you are in agentic mode, offer to do any or all of: - mark the pull request as ready, - assign the issue to the human if it is not already assigned, - - _concisely_ comment on the issue explaining the change, indicating you are an LLM. + - _concisely_ comment on the issue explaining the change, indicating you are GitHub Copilot. From 290d60c3ddfe9275dfb4477fef277a70303c8dec Mon Sep 17 00:00:00 2001 From: Kevin Heis Date: Fri, 6 Feb 2026 10:57:38 -0800 Subject: [PATCH 2/2] Replace explicit `any` types with proper TypeScript types (#59127) Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- .../lib/linting-rules/rai-reusable-usage.ts | 2 +- src/content-render/scripts/cta-builder.ts | 57 ++++++++++++++----- src/content-render/tests/liquid-helpers.ts | 7 +-- src/content-render/tests/liquid-tags.ts | 7 ++- src/events/components/is-headless.ts | 2 +- .../middleware/context/product-examples.ts | 2 +- src/frame/tests/find-page.ts | 3 +- .../get-automatic-request-logger.ts | 6 +- src/search/components/input/SearchContext.tsx | 7 ++- .../routes/ai-search-autocomplete-route.ts | 2 +- src/tests/helpers/script-data.ts | 21 +++++-- src/tests/mocks/cse-copilot-mock.ts | 2 +- src/tests/mocks/start-mock-server.ts | 5 +- .../lib/enterprise-server-releases.ts | 2 +- 14 files changed, 85 insertions(+), 40 deletions(-) diff --git a/src/content-linter/lib/linting-rules/rai-reusable-usage.ts b/src/content-linter/lib/linting-rules/rai-reusable-usage.ts index 56ce74eb1cc1..05268c8d06b7 100644 --- a/src/content-linter/lib/linting-rules/rai-reusable-usage.ts +++ b/src/content-linter/lib/linting-rules/rai-reusable-usage.ts @@ -10,7 +10,7 @@ import type { RuleParams, RuleErrorCallback, Rule } from '../../types' interface Frontmatter { type?: string // Allow any additional frontmatter properties since we only care about 'type' - [key: string]: any + [key: string]: unknown } interface LiquidToken { diff --git a/src/content-render/scripts/cta-builder.ts b/src/content-render/scripts/cta-builder.ts index 1d1f3eb3038e..bd633c63dbb6 100644 --- a/src/content-render/scripts/cta-builder.ts +++ b/src/content-render/scripts/cta-builder.ts @@ -18,6 +18,19 @@ interface CTAParams { ref_style?: string } +const CTA_PARAM_KEYS: (keyof CTAParams)[] = ['ref_product', 'ref_plan', 'ref_type', 'ref_style'] + +interface CTASchemaProperty { + type: string + name: string + description: string + enum: string[] +} + +type CTASchemaProperties = { + [K in keyof CTAParams]-?: CTASchemaProperty +} + // Conversion mappings from old CTA format to new schema const ctaToTypeMapping: Record = { 'GHEC trial': 'trial', @@ -153,27 +166,40 @@ function extractCTAParams(url: string): CTAParams { const urlObj = new URL(url) const ctaParams: CTAParams = {} for (const [key, value] of urlObj.searchParams.entries()) { - if (key.startsWith('ref_')) { - ;(ctaParams as any)[key] = value + if (key.startsWith('ref_') && CTA_PARAM_KEYS.includes(key as keyof CTAParams)) { + ctaParams[key as keyof CTAParams] = value } } return ctaParams } +interface AjvErrorParams { + missingProperty?: string + allowedValues?: string[] + additionalProperty?: string +} + +interface AjvError { + keyword: string + instancePath: string + message?: string + params: AjvErrorParams +} + // Process AJV validation errors into readable messages -function formatValidationErrors(ctaParams: CTAParams, errors: any[]): string[] { +function formatValidationErrors(ctaParams: CTAParams, errors: AjvError[]): string[] { const errorMessages: string[] = [] for (const error of errors) { let message = '' if (error.keyword === 'required') { - message = `Missing required parameter: ${(error.params as any)?.missingProperty}` + message = `Missing required parameter: ${error.params.missingProperty}` } else if (error.keyword === 'enum') { const paramName = error.instancePath.substring(1) const invalidValue = ctaParams[paramName as keyof CTAParams] - const allowedValues = (error.params as any)?.allowedValues || [] + const allowedValues = error.params.allowedValues || [] message = `Invalid value for ${paramName}: "${invalidValue}". Valid values are: ${allowedValues.join(', ')}` } else if (error.keyword === 'additionalProperties') { - message = `Unexpected parameter: ${(error.params as any)?.additionalProperty}` + message = `Unexpected parameter: ${error.params.additionalProperty}` } else { message = `Validation error: ${error.message}` } @@ -191,7 +217,7 @@ function validateCTAParams(params: CTAParams): { isValid: boolean; errors: strin return { isValid: true, errors: [] } } - const errors = formatValidationErrors(params, ajvErrors) + const errors = formatValidationErrors(params, ajvErrors as unknown as AjvError[]) return { isValid: false, errors, @@ -382,12 +408,14 @@ async function interactiveBuilder(): Promise { // Required parameters console.log(chalk.white(`\nRequired parameters:`)) + const schemaProps = ctaSchema.properties as CTASchemaProperties for (const requiredParam of ctaSchema.required) { - ;(params as any)[requiredParam] = await selectFromOptions( + const paramKey = requiredParam as keyof CTAParams + params[paramKey] = await selectFromOptions( requiredParam, - (ctaSchema.properties as any)[requiredParam].description, - (ctaSchema.properties as any)[requiredParam].enum, + schemaProps[paramKey].description, + schemaProps[paramKey].enum, prompt, ) } @@ -399,15 +427,16 @@ async function interactiveBuilder(): Promise { const optionalProperties = allProperties.filter((prop) => !ctaSchema.required.includes(prop)) for (const optionalParam of optionalProperties) { + const paramKey = optionalParam as keyof CTAParams const includeParam = await confirmChoice( - `Include ${(ctaSchema.properties as any)[optionalParam].name.toLowerCase()}?`, + `Include ${schemaProps[paramKey].name.toLowerCase()}?`, prompt, ) if (includeParam) { - ;(params as any)[optionalParam] = await selectFromOptions( + params[paramKey] = await selectFromOptions( optionalParam, - (ctaSchema.properties as any)[optionalParam].description, - (ctaSchema.properties as any)[optionalParam].enum, + schemaProps[paramKey].description, + schemaProps[paramKey].enum, prompt, ) } diff --git a/src/content-render/tests/liquid-helpers.ts b/src/content-render/tests/liquid-helpers.ts index 9cc9fd671b29..eb02f4fd84bc 100644 --- a/src/content-render/tests/liquid-helpers.ts +++ b/src/content-render/tests/liquid-helpers.ts @@ -3,14 +3,13 @@ import { afterAll, beforeAll, describe, expect, test, vi } from 'vitest' import { liquid } from '@/content-render/index' import languages from '@/languages/lib/languages-server' import { DataDirectory } from '@/tests/helpers/data-directory' +import type { Context } from '@/types' describe('liquid helper tags', () => { vi.setConfig({ testTimeout: 60 * 1000 }) - // Using 'any' type as context is a test fixture with dynamic properties set in beforeAll - const context: any = {} - // Using 'any' type as DataDirectory is from data-directory.ts which lacks type definitions - let dd: any + const context: Partial = {} + let dd: DataDirectory const enDirBefore = languages.en.dir beforeAll(() => { diff --git a/src/content-render/tests/liquid-tags.ts b/src/content-render/tests/liquid-tags.ts index 5505f28c43c5..0209be92c090 100644 --- a/src/content-render/tests/liquid-tags.ts +++ b/src/content-render/tests/liquid-tags.ts @@ -31,9 +31,10 @@ describe('liquid-tags script integration tests', () => { stdio: 'pipe', timeout: 30000, }) - } catch (error: any) { - output = error.stdout + error.stderr - exitCode = error.status || 1 + } catch (error: unknown) { + const execError = error as { stdout?: string; stderr?: string; status?: number } + output = (execError.stdout || '') + (execError.stderr || '') + exitCode = execError.status || 1 } return { output, exitCode } diff --git a/src/events/components/is-headless.ts b/src/events/components/is-headless.ts index 048647f67792..6f72df0a05af 100644 --- a/src/events/components/is-headless.ts +++ b/src/events/components/is-headless.ts @@ -2,7 +2,7 @@ declare global { interface Window { - GHDOCSPLAYWRIGHT: any + GHDOCSPLAYWRIGHT: boolean | number } } diff --git a/src/frame/middleware/context/product-examples.ts b/src/frame/middleware/context/product-examples.ts index a40b0a4390f2..67143cc1bc35 100644 --- a/src/frame/middleware/context/product-examples.ts +++ b/src/frame/middleware/context/product-examples.ts @@ -16,7 +16,7 @@ function getProductExampleData( try { return getDataByLanguage(`product-examples.${product}.${key}`, language) } catch (error) { - if (error instanceof Error && (error as any).code === 'ENOENT') return + if (error instanceof Error && 'code' in error && error.code === 'ENOENT') return throw error } } diff --git a/src/frame/tests/find-page.ts b/src/frame/tests/find-page.ts index 60189ae9f874..8ecc9b24a282 100644 --- a/src/frame/tests/find-page.ts +++ b/src/frame/tests/find-page.ts @@ -5,6 +5,7 @@ import { describe, expect, test, vi } from 'vitest' import Page from '@/frame/lib/page' import findPage from '@/frame/lib/find-page' +import type { Page as PageType } from '@/types' const __dirname = path.dirname(fileURLToPath(import.meta.url)) @@ -31,7 +32,7 @@ describe('find page', () => { const redirectedPage = findPage( redirectToFind, - pageMap as any, // Using any due to type conflicts between different Page type definitions + pageMap as unknown as Record, page.buildRedirects(), ) expect(redirectedPage).toBeDefined() diff --git a/src/observability/logger/middleware/get-automatic-request-logger.ts b/src/observability/logger/middleware/get-automatic-request-logger.ts index 0ea1992178bb..0153254d7e63 100644 --- a/src/observability/logger/middleware/get-automatic-request-logger.ts +++ b/src/observability/logger/middleware/get-automatic-request-logger.ts @@ -33,7 +33,7 @@ export function getAutomaticRequestLogger() { const originalEnd = res.end // Override res.end to log when response completes - res.end = function (chunk?: any, encoding?: any) { + res.end = function (...args: unknown[]) { const responseTime = Date.now() - startTime const status = res.statusCode || 200 const contentLength = res.getHeader('content-length') || '-' @@ -59,7 +59,7 @@ export function getAutomaticRequestLogger() { // Don't log `/_next/` requests unless LOG_LEVEL is `debug` or higher if (url?.startsWith('/_next/') && logLevelNum < 3) { - return originalEnd.call(this, chunk, encoding) + return originalEnd.apply(this, args as Parameters) } // Choose color based on status code @@ -80,7 +80,7 @@ export function getAutomaticRequestLogger() { } // Call the original end method to complete the response - return originalEnd.call(this, chunk, encoding) + return originalEnd.apply(this, args as Parameters) } next() diff --git a/src/search/components/input/SearchContext.tsx b/src/search/components/input/SearchContext.tsx index b1fe3ae3c137..2e7e3d8cc87f 100644 --- a/src/search/components/input/SearchContext.tsx +++ b/src/search/components/input/SearchContext.tsx @@ -2,6 +2,11 @@ import { createContext, useContext, RefObject, SetStateAction, MutableRefObject import type { AIReference } from '../types' import type { AutocompleteSearchHit, GeneralSearchHit } from '@/search/types' +export type TranslationFunction = ( + strings: TemplateStringsArray | string, + ...values: unknown[] +) => string + export interface AutocompleteSearchHitWithUserQuery extends AutocompleteSearchHit { isUserQuery?: boolean } @@ -29,7 +34,7 @@ export interface AskAIState { } export interface SearchContextType { - t: any + t: TranslationFunction generalSearchOptions: GeneralSearchHitWithOptions[] aiOptionsWithUserInput: AutocompleteSearchHitWithUserQuery[] generalSearchResultOnSelect: (selectedOption: GeneralSearchHit) => void diff --git a/src/search/lib/routes/ai-search-autocomplete-route.ts b/src/search/lib/routes/ai-search-autocomplete-route.ts index 1b421afe1bae..6a6310f5ab60 100644 --- a/src/search/lib/routes/ai-search-autocomplete-route.ts +++ b/src/search/lib/routes/ai-search-autocomplete-route.ts @@ -10,7 +10,7 @@ export async function aiSearchAutocompleteRoute(req: Request, res: Response) { // If no query is provided, we want to return the top 5 most popular terms // This is a special case for AI search autocomplete // So we use `force` to allow the query to be empty without the usual validation error - const force = {} as any + const force: { query?: string } = {} if (!req.query.query) { force.query = '' } diff --git a/src/tests/helpers/script-data.ts b/src/tests/helpers/script-data.ts index ec028b852382..f28f6b7c7ad1 100644 --- a/src/tests/helpers/script-data.ts +++ b/src/tests/helpers/script-data.ts @@ -1,15 +1,24 @@ +import cheerio from 'cheerio' + const NEXT_DATA_QUERY = 'script#__NEXT_DATA__' const PRIMER_DATA_QUERY = 'script#__PRIMER_DATA__' -// Using any type for $ parameter as it represents a jQuery-like selector (cheerio) -function getScriptData($: any, key: string): any { +function getScriptData($: ReturnType, key: string): unknown { const data = $(key) if (data.length !== 1) { throw new Error(`Not exactly 1 element match for '${key}'. Found ${data.length}`) } - return JSON.parse(data.get()[0].children[0].data) + const element = data.get()[0] + if (element && 'children' in element) { + const firstChild = element.children[0] + if (firstChild && 'data' in firstChild) { + return JSON.parse(firstChild.data as string) + } + } + throw new Error(`Could not extract data from '${key}'`) } -// Using any types for cheerio/jQuery-like objects and parsed JSON data -export const getNextData = ($: any): any => getScriptData($, NEXT_DATA_QUERY) -export const getPrimerData = ($: any): any => getScriptData($, PRIMER_DATA_QUERY) +export const getNextData = ($: ReturnType): unknown => + getScriptData($, NEXT_DATA_QUERY) +export const getPrimerData = ($: ReturnType): unknown => + getScriptData($, PRIMER_DATA_QUERY) diff --git a/src/tests/mocks/cse-copilot-mock.ts b/src/tests/mocks/cse-copilot-mock.ts index e2b75a17c39a..4163d2d22b3b 100644 --- a/src/tests/mocks/cse-copilot-mock.ts +++ b/src/tests/mocks/cse-copilot-mock.ts @@ -31,7 +31,7 @@ export function cseCopilotPostAnswersMock(req: Request, res: Response) { } // Function to send a chunk with proper encoding - const sendEncodedChunk = (data: any, isLast = false) => { + const sendEncodedChunk = (data: string, isLast = false) => { const prefix = isLast ? '' : '\n' // Optionally, add delimiters if needed const buffer = Buffer.from(prefix + data, 'utf-8') res.write(buffer) diff --git a/src/tests/mocks/start-mock-server.ts b/src/tests/mocks/start-mock-server.ts index 93c01f5ba2a8..4ee1c493d062 100644 --- a/src/tests/mocks/start-mock-server.ts +++ b/src/tests/mocks/start-mock-server.ts @@ -21,6 +21,7 @@ You should override the variable in the overrideEnvForTesting function in this */ import express from 'express' +import type { Server } from 'http' import { CSE_COPILOT_PREFIX, cseCopilotPostAnswersMock } from './cse-copilot-mock' // Define the default port for the mock server @@ -30,7 +31,7 @@ const MOCK_SERVER_PORT = 3012 const serverUrl = `http://localhost:${MOCK_SERVER_PORT}` // Variable to hold the server instance -let server: any = null +let server: Server | null = null // Override environment variables for testing purposes export function overrideEnvForTesting() { @@ -55,7 +56,7 @@ export function startMockServer(port = MOCK_SERVER_PORT) { export function stopMockServer(): Promise { return new Promise((resolve, reject) => { if (server) { - server.close((err: any) => { + server.close((err: Error | undefined) => { if (err) { console.error('Error stopping the mock server:', err) reject(err) diff --git a/src/versions/lib/enterprise-server-releases.ts b/src/versions/lib/enterprise-server-releases.ts index 7a4772b7b417..1d9dd692d85e 100644 --- a/src/versions/lib/enterprise-server-releases.ts +++ b/src/versions/lib/enterprise-server-releases.ts @@ -8,7 +8,7 @@ interface VersionDateData { releaseCandidateDate?: string generalAvailabilityDate?: string deprecationDate: string - [key: string]: any + [key: string]: unknown } interface EnhancedVersionDateData extends VersionDateData {