.uses` will be parsed as dependencies. For more information, see [AUTOTITLE](/actions/using-workflows/workflow-syntax-for-github-actions).
> * {% data reusables.dependabot.dependabot-alert-actions-semver %} For more information, see [AUTOTITLE](/code-security/dependabot/dependabot-alerts/about-dependabot-alerts) and [AUTOTITLE](/code-security/dependabot/dependabot-version-updates/about-dependabot-version-updates).
diff --git a/data/reusables/enterprise_clustering/key-value-pair-order-irrelevant.md b/data/reusables/enterprise_clustering/key-value-pair-order-irrelevant.md
deleted file mode 100644
index fb58841728ed..000000000000
--- a/data/reusables/enterprise_clustering/key-value-pair-order-irrelevant.md
+++ /dev/null
@@ -1 +0,0 @@
-The order of the key-value pairs doesn't matter.
diff --git a/data/reusables/enterprise_clustering/replacing-a-cluster-node-mark-offline.md b/data/reusables/enterprise_clustering/replacing-a-cluster-node-mark-offline.md
deleted file mode 100644
index 37255b65966a..000000000000
--- a/data/reusables/enterprise_clustering/replacing-a-cluster-node-mark-offline.md
+++ /dev/null
@@ -1,11 +0,0 @@
-1. To mark the failed node offline, on any node, modify the [cluster configuration file](/admin/enterprise-management/configuring-clustering/initializing-the-cluster#about-the-cluster-configuration-file) (`cluster.conf`) in the relevant node section to include the text `offline = true`.
-
- For example, this modified `cluster.conf` will mark the `ghe-data-node-3` node as offline:
-
-
- [cluster "ghe-data-node-3"]
- hostname = ghe-data-node-3
- offline = true
- ipv4 = 192.168.0.6
- # ipv6 = fd12:3456:789a:1::6
-
diff --git a/data/reusables/enterprise_clustering/replacing-a-cluster-node-need-three-nodes.md b/data/reusables/enterprise_clustering/replacing-a-cluster-node-need-three-nodes.md
deleted file mode 100644
index 010905ff71be..000000000000
--- a/data/reusables/enterprise_clustering/replacing-a-cluster-node-need-three-nodes.md
+++ /dev/null
@@ -1 +0,0 @@
-1. If you're taking a node offline that provides data services, such as `git-server`, `pages-server`, or `storage-server`, evacuate the node. For more information, see [AUTOTITLE](/admin/enterprise-management/configuring-clustering/evacuating-a-cluster-node-running-data-services).
diff --git a/data/reusables/enterprise_clustering/replacing-a-cluster-node-replacement-name.md b/data/reusables/enterprise_clustering/replacing-a-cluster-node-replacement-name.md
deleted file mode 100644
index 8746dadde7aa..000000000000
--- a/data/reusables/enterprise_clustering/replacing-a-cluster-node-replacement-name.md
+++ /dev/null
@@ -1,10 +0,0 @@
-1. If you're replacing the primary Redis node, in `cluster.conf`, modify the `redis-master` value with the replacement node name.
-
- > [!NOTE]
- > If your primary Redis node is also your primary MySQL node, see [Replacing the primary database node](#replacing-the-primary-database-node-mysql-or-mysql-and-mssql).
-
- For example, this modified `cluster.conf` file specifies a newly provisioned cluster node, `ghe-replacement-data-node-1` as the primary Redis node:
-
-
- redis-master = ghe-replacement-data-node-1
-
diff --git a/data/reusables/enterprise_clustering/replacing-a-cluster-node-validate-config.md b/data/reusables/enterprise_clustering/replacing-a-cluster-node-validate-config.md
deleted file mode 100644
index 8634ab4903af..000000000000
--- a/data/reusables/enterprise_clustering/replacing-a-cluster-node-validate-config.md
+++ /dev/null
@@ -1 +0,0 @@
-1. From the administrative shell of the node where you modified `cluster.conf`, run `ghe-cluster-config-apply`. This will validate the configuration file, copy it to each node in the cluster, and mark the node offline.
diff --git a/data/reusables/enterprise_site_admin_settings/click-preview.md b/data/reusables/enterprise_site_admin_settings/click-preview.md
deleted file mode 100644
index 39a8d3eaf452..000000000000
--- a/data/reusables/enterprise_site_admin_settings/click-preview.md
+++ /dev/null
@@ -1 +0,0 @@
-1. To see the rendered message, click **Preview**.
diff --git a/src/audit-logs/data/fpt/organization.json b/src/audit-logs/data/fpt/organization.json
index 1bff0a04481f..46e95fb5a73b 100644
--- a/src/audit-logs/data/fpt/organization.json
+++ b/src/audit-logs/data/fpt/organization.json
@@ -369,6 +369,11 @@
"description": "Dependabot alerts were enabled for all new repositories.",
"docs_reference_links": "/organizations/keeping-your-organization-secure/managing-security-settings-for-your-organization/managing-security-and-analysis-settings-for-your-organization#enabling-or-disabling-a-feature-automatically-when-new-repositories-are-added"
},
+ {
+ "action": "dependabot_repository_access.default_access_level_updated",
+ "description": "The default repository access for Dependabot was updated.",
+ "docs_reference_links": "N/A"
+ },
{
"action": "dependabot_repository_access.repositories_updated",
"description": "The repositories that Dependabot can access were updated.",
diff --git a/src/audit-logs/data/ghec/enterprise.json b/src/audit-logs/data/ghec/enterprise.json
index 795693e8481c..9d52f61fb56c 100644
--- a/src/audit-logs/data/ghec/enterprise.json
+++ b/src/audit-logs/data/ghec/enterprise.json
@@ -989,6 +989,11 @@
"description": "Dependabot alerts were enabled for all new repositories.",
"docs_reference_links": "/organizations/keeping-your-organization-secure/managing-security-settings-for-your-organization/managing-security-and-analysis-settings-for-your-organization#enabling-or-disabling-a-feature-automatically-when-new-repositories-are-added"
},
+ {
+ "action": "dependabot_repository_access.default_access_level_updated",
+ "description": "The default repository access for Dependabot was updated.",
+ "docs_reference_links": "N/A"
+ },
{
"action": "dependabot_repository_access.repositories_updated",
"description": "The repositories that Dependabot can access were updated.",
diff --git a/src/audit-logs/data/ghec/organization.json b/src/audit-logs/data/ghec/organization.json
index 1bff0a04481f..46e95fb5a73b 100644
--- a/src/audit-logs/data/ghec/organization.json
+++ b/src/audit-logs/data/ghec/organization.json
@@ -369,6 +369,11 @@
"description": "Dependabot alerts were enabled for all new repositories.",
"docs_reference_links": "/organizations/keeping-your-organization-secure/managing-security-settings-for-your-organization/managing-security-and-analysis-settings-for-your-organization#enabling-or-disabling-a-feature-automatically-when-new-repositories-are-added"
},
+ {
+ "action": "dependabot_repository_access.default_access_level_updated",
+ "description": "The default repository access for Dependabot was updated.",
+ "docs_reference_links": "N/A"
+ },
{
"action": "dependabot_repository_access.repositories_updated",
"description": "The repositories that Dependabot can access were updated.",
diff --git a/src/audit-logs/lib/config.json b/src/audit-logs/lib/config.json
index fcbe8b94b973..4d7f6b5b1368 100644
--- a/src/audit-logs/lib/config.json
+++ b/src/audit-logs/lib/config.json
@@ -3,5 +3,5 @@
"apiOnlyEvents": "This event is not available in the web interface, only via the REST API, audit log streaming, or JSON/CSV exports.",
"apiRequestEvent": "This event is only available via audit log streaming."
},
- "sha": "961356f4c2523591e15b4f0cae039ee834edfb70"
+ "sha": "03efba5e096f01d4d6e7d2ea9dfaf06a8128fe60"
}
\ No newline at end of file
diff --git a/src/events/components/events.ts b/src/events/components/events.ts
index bebb5c272ace..f968baa699cf 100644
--- a/src/events/components/events.ts
+++ b/src/events/components/events.ts
@@ -86,8 +86,6 @@ export function sendEvent({
eventGroupKey?: string
eventGroupId?: string
} & EventPropsByType[T]) {
- if (isHeadless()) return
-
const body = {
type,
@@ -118,6 +116,7 @@ export function sendEvent({
// Device information
// os, os_version, browser, browser_version:
...parseUserAgent(),
+ is_headless: isHeadless(),
viewport_width: document.documentElement.clientWidth,
viewport_height: document.documentElement.clientHeight,
diff --git a/src/events/components/experiments/experiments.ts b/src/events/components/experiments/experiments.ts
index 050d134e6936..6d4e1b87df1f 100644
--- a/src/events/components/experiments/experiments.ts
+++ b/src/events/components/experiments/experiments.ts
@@ -24,11 +24,6 @@ export const EXPERIMENTS = {
percentOfUsersToGetExperiment: 0, // 10% of users will get the experiment
includeVariationInContext: true, // All events will include the `experiment_variation` of the `ai_search_experiment`
limitToLanguages: ['en'], // Only users with the `en` language will be included in the experiment
- limitToVersions: [
- 'free-pro-team@latest',
- 'enterprise-cloud@latest',
- 'enterprise-server@latest',
- ], // Only enable for versions
alwaysShowForStaff: true, // When set to true, staff will always see the experiment (determined by the `staffonly` cookie)
turnOnWithURLParam: 'ai_search', /// When the query param `?feature=ai_search` is set, the experiment will be enabled
},
diff --git a/src/events/lib/schema.ts b/src/events/lib/schema.ts
index 55ad26277ce2..deaa0790fd0e 100644
--- a/src/events/lib/schema.ts
+++ b/src/events/lib/schema.ts
@@ -136,6 +136,9 @@ const context = {
type: 'string',
description: 'The version of the browser the user is browsing with.',
},
+ is_headless: {
+ type: 'boolean',
+ },
viewport_width: {
type: 'number',
description: 'The viewport width, not the overall device size.',
diff --git a/src/events/tests/middleware.ts b/src/events/tests/middleware.ts
index 6da5601bdfd4..f86bde5741ad 100644
--- a/src/events/tests/middleware.ts
+++ b/src/events/tests/middleware.ts
@@ -41,6 +41,7 @@ describe('POST /events', () => {
os_version: '18.04',
browser: 'chrome',
browser_version: '85.0.4183.121',
+ is_headless: false,
viewport_width: 1418,
viewport_height: 501,
@@ -72,6 +73,7 @@ describe('POST /events', () => {
os_version: '18.04',
browser: 'chrome',
browser_version: '85.0.4183.121',
+ is_headless: false,
viewport_width: 1418,
viewport_height: 501,
diff --git a/src/events/types.ts b/src/events/types.ts
index 8ceb69b8cfab..762dbc9a8df5 100644
--- a/src/events/types.ts
+++ b/src/events/types.ts
@@ -50,6 +50,7 @@ export type EventProps = {
code_display_preference: string
event_group_key?: string
event_group_id?: string
+ is_headless: boolean
}
}
diff --git a/src/search/components/helpers/execute-search-actions.ts b/src/search/components/helpers/execute-search-actions.ts
index 24809ef0ca37..8c746101dc51 100644
--- a/src/search/components/helpers/execute-search-actions.ts
+++ b/src/search/components/helpers/execute-search-actions.ts
@@ -45,18 +45,10 @@ export function executeGeneralSearch(
router.push(asPath, undefined, { shallow: false })
}
-export async function executeAISearch(
- router: NextRouter,
- version: string,
- query: string,
- debug = false,
-) {
- let language = router.locale || 'en'
-
+export async function executeAISearch(version: string, query: string, debug = false) {
const body = {
query,
version,
- language,
...(debug && { debug: '1' }),
}
diff --git a/src/search/components/input/AskAIResults.tsx b/src/search/components/input/AskAIResults.tsx
index 9ebc584b1134..d374466f9af9 100644
--- a/src/search/components/input/AskAIResults.tsx
+++ b/src/search/components/input/AskAIResults.tsx
@@ -167,7 +167,7 @@ export function AskAIResults({
let conversationIdBuffer = ''
try {
- const response = await executeAISearch(router, version, query, debug)
+ const response = await executeAISearch(version, query, debug)
if (!response.ok) {
// If there is JSON and the `upstreamStatus` key, the error is from the upstream sever (CSE)
let responseJson
diff --git a/src/search/lib/ai-search-proxy.ts b/src/search/lib/ai-search-proxy.ts
index d7a925955f64..7c2384039a1c 100644
--- a/src/search/lib/ai-search-proxy.ts
+++ b/src/search/lib/ai-search-proxy.ts
@@ -1,11 +1,12 @@
-import { Request, Response } from 'express'
+import { Response } from 'express'
import statsd from '@/observability/lib/statsd'
import got from 'got'
import { getHmacWithEpoch } from '@/search/lib/helpers/get-cse-copilot-auth'
import { getCSECopilotSource } from '@/search/lib/helpers/cse-copilot-docs-versions'
+import type { ExtendedRequest } from '@/types'
-export const aiSearchProxy = async (req: Request, res: Response) => {
- const { query, version, language } = req.body
+export const aiSearchProxy = async (req: ExtendedRequest, res: Response) => {
+ const { query, version } = req.body
const errors = []
@@ -15,18 +16,12 @@ export const aiSearchProxy = async (req: Request, res: Response) => {
} else if (typeof query !== 'string') {
errors.push({ message: `Invalid 'query' in request body. Must be a string` })
}
- if (!version) {
- errors.push({ message: `Missing required key 'version' in request body` })
- }
- if (!language) {
- errors.push({ message: `Missing required key 'language' in request body` })
- }
let docsSource = ''
try {
- docsSource = getCSECopilotSource(version, language)
+ docsSource = getCSECopilotSource(version)
} catch (error: any) {
- errors.push({ message: error?.message || 'Invalid version or language' })
+ errors.push({ message: error?.message || 'Invalid version' })
}
if (errors.length) {
@@ -36,7 +31,7 @@ export const aiSearchProxy = async (req: Request, res: Response) => {
const diagnosticTags = [
`version:${version}`.slice(0, 200),
- `language:${language}`.slice(0, 200),
+ `language:${req.language}`.slice(0, 200),
`queryLength:${query.length}`.slice(0, 200),
]
statsd.increment('ai-search.call', 1, diagnosticTags)
@@ -52,7 +47,8 @@ export const aiSearchProxy = async (req: Request, res: Response) => {
}
try {
- const stream = got.stream.post(`${process.env.CSE_COPILOT_ENDPOINT}/answers`, {
+ // TODO: We temporarily add ?ai_search=1 to use a new pattern in cgs-copilot production
+ const stream = got.stream.post(`${process.env.CSE_COPILOT_ENDPOINT}/answers?ai_search=1`, {
json: body,
headers: {
Authorization: getHmacWithEpoch(),
diff --git a/src/search/lib/elasticsearch-indexes.ts b/src/search/lib/elasticsearch-indexes.ts
index 4990d229a97b..349215ecde3b 100644
--- a/src/search/lib/elasticsearch-indexes.ts
+++ b/src/search/lib/elasticsearch-indexes.ts
@@ -74,7 +74,15 @@ export function getElasticSearchIndex(
}
// e.g. free-pro-team becomes fpt for the index name
- const indexVersion = versionToIndexVersionMap[version]
+ let indexVersion = versionToIndexVersionMap[version]
+
+ // TODO: For AI Search, we initially only supported the latest GHES version
+ // Supporting more versions would involve adding more indexes and generating the data to fill them
+ // As a work around, we will just use the latest version for all GHES suggestions / autocomplete
+ // This is a temporary fix until we can support more versions
+ if (type === 'aiSearchAutocomplete' && indexVersion.startsWith('ghes')) {
+ indexVersion = versionToIndexVersionMap['enterprise-server']
+ }
// In the index-test-fixtures.sh script, we use the tests_ prefix index for testing
const testPrefix = process.env.NODE_ENV === 'test' ? 'tests_' : ''
diff --git a/src/search/lib/helpers/cse-copilot-docs-versions.ts b/src/search/lib/helpers/cse-copilot-docs-versions.ts
index 8fe490f032e0..eb2e96729785 100644
--- a/src/search/lib/helpers/cse-copilot-docs-versions.ts
+++ b/src/search/lib/helpers/cse-copilot-docs-versions.ts
@@ -1,5 +1,4 @@
// Versions used by cse-copilot
-import { allVersions } from '@/versions/lib/all-versions'
import { versionToIndexVersionMap } from '../elasticsearch-versions'
const CSE_COPILOT_DOCS_VERSIONS = ['dotcom', 'ghec', 'ghes']
@@ -9,70 +8,22 @@ export function supportedCSECopilotLanguages() {
return DOCS_LANGUAGES
}
-export function getCSECopilotSource(
- version: (typeof CSE_COPILOT_DOCS_VERSIONS)[number],
- language: (typeof DOCS_LANGUAGES)[number],
-) {
- const mappedVersion = versionToIndexVersionMap[version]
- const { cseCopilotDocsVersion, ghesButNotLatest } = getVersionInfo(mappedVersion)
+export function getCSECopilotSource(version: (typeof CSE_COPILOT_DOCS_VERSIONS)[number]) {
+ if (!version) {
+ throw new Error(`Missing required key 'version' in request body`)
+ }
- if (ghesButNotLatest) {
- throw new Error(
- `Only the latest version of GHES is supported for cse-copilot queries. Please use 'ghes@latest'`,
- )
+ let mappedVersion = versionToIndexVersionMap[version]
+ // CSE-Copilot uses 'dotcom' as the version name for free-pro-team
+ if (mappedVersion === 'fpt') {
+ mappedVersion = 'dotcom'
}
- if (!CSE_COPILOT_DOCS_VERSIONS.includes(cseCopilotDocsVersion)) {
+ if (!CSE_COPILOT_DOCS_VERSIONS.includes(mappedVersion) && !mappedVersion?.startsWith('ghes-')) {
throw new Error(
`Invalid 'version' in request body: '${version}'. Must be one of: ${CSE_COPILOT_DOCS_VERSIONS.join(', ')}`,
)
}
- if (!DOCS_LANGUAGES.includes(language)) {
- throw new Error(
- `Invalid 'language' in request body '${language}'. Must be one of: ${DOCS_LANGUAGES.join(', ')}`,
- )
- }
- // cse-copilot uses version names in the form `docs__`, e.g. `docs_ghes_en`
- return `docs_${cseCopilotDocsVersion}_${language}`
-}
-
-function getVersionInfo(Version: string): {
- cseCopilotDocsVersion: string
- ghesButNotLatest: boolean
-} {
- const versionObject = Object.values(allVersions).find(
- (info) =>
- info.shortName === Version ||
- info.plan === Version ||
- info.miscVersionName === Version ||
- info.currentRelease === Version,
- )
-
- let cseCopilotDocsVersion = versionObject?.shortName || ''
- let ghesButNotLatest = false
- if (!versionObject || !cseCopilotDocsVersion) {
- return {
- cseCopilotDocsVersion,
- ghesButNotLatest,
- }
- }
-
- // CSE-Copilot uses 'dotcom' as the version name for free-pro-team
- if (cseCopilotDocsVersion === 'fpt') {
- cseCopilotDocsVersion = 'dotcom'
- }
-
- // If ghes, we only support the latest version for cse-copilot queries
- // Since that's the only version cse-copilot scrapes from our docs
- if (
- versionObject.shortName === 'ghes' &&
- versionObject.currentRelease !== versionObject.latestRelease
- ) {
- ghesButNotLatest = true
- }
-
- return {
- cseCopilotDocsVersion,
- ghesButNotLatest,
- }
+ // cse-copilot uses version names in the form `docs_`, e.g. `docs_ghes-3.16`
+ return `docs_${mappedVersion}`
}
diff --git a/src/search/lib/search-request-params/search-params-objects.ts b/src/search/lib/search-request-params/search-params-objects.ts
index 0fb8fa5e7cae..356e0ff9c90c 100644
--- a/src/search/lib/search-request-params/search-params-objects.ts
+++ b/src/search/lib/search-request-params/search-params-objects.ts
@@ -6,7 +6,6 @@
import languages from '@/languages/lib/languages'
import { allIndexVersionKeys, versionToIndexVersionMap } from '@/search/lib/elasticsearch-versions'
import { SearchTypes } from '@/search/types'
-import { latest } from '@/versions/lib/enterprise-server-releases'
import type { SearchRequestQueryParams } from '@/search/lib/search-request-params/types'
@@ -120,20 +119,14 @@ const SHARED_AUTOCOMPLETE_PARAMS_OBJ: SearchRequestQueryParams[] = [
cast: (size: string) => parseInt(size, 10),
validate: (size: number) => size >= 0 && size <= MAX_AUTOCOMPLETE_SIZE,
},
- // We only want to enable for latest versions of fpt, ghec, and ghes
{
key: 'version',
default_: 'free-pro-team',
validate: (version: string) => {
- const mappedVersion = versionToIndexVersionMap[version]
- if (
- mappedVersion === 'fpt' ||
- mappedVersion === 'ghec' ||
- mappedVersion === `ghes-${latest}`
- ) {
- return true
+ if (!versionToIndexVersionMap[version]) {
+ throw new ValidationError(`'${version}' not in ${allIndexVersionKeys.join(', ')}`)
}
- return false
+ return true
},
},
]
diff --git a/src/search/tests/api-ai-search.ts b/src/search/tests/api-ai-search.ts
index 9b66f2c6db30..faba979a138a 100644
--- a/src/search/tests/api-ai-search.ts
+++ b/src/search/tests/api-ai-search.ts
@@ -94,24 +94,8 @@ describe('AI Search Routes', () => {
])
})
- test('should handle validation errors: language missing', async () => {
- let body = { query: 'example query', version: 'dotcom' }
- const response = await post('/api/ai-search/v1', {
- body: JSON.stringify(body),
- headers: { 'Content-Type': 'application/json' },
- })
-
- const responseBody = JSON.parse(response.body)
-
- expect(response.ok).toBe(false)
- expect(responseBody['errors']).toEqual([
- { message: `Missing required key 'language' in request body` },
- { message: `Invalid 'language' in request body 'undefined'. Must be one of: en` },
- ])
- })
-
test('should handle validation errors: version missing', async () => {
- let body = { query: 'example query', language: 'en' }
+ let body = { query: 'example query' }
const response = await post('/api/ai-search/v1', {
body: JSON.stringify(body),
headers: { 'Content-Type': 'application/json' },
@@ -122,13 +106,10 @@ describe('AI Search Routes', () => {
expect(response.ok).toBe(false)
expect(responseBody['errors']).toEqual([
{ message: `Missing required key 'version' in request body` },
- {
- message: `Invalid 'version' in request body: 'undefined'. Must be one of: dotcom, ghec, ghes`,
- },
])
})
- test('should handle multiple validation errors: query missing, invalid language and version', async () => {
+ test('should handle multiple validation errors: query missing and version', async () => {
let body = { language: 'fr', version: 'fpt' }
const response = await post('/api/ai-search/v1', {
body: JSON.stringify(body),
@@ -140,9 +121,6 @@ describe('AI Search Routes', () => {
expect(response.ok).toBe(false)
expect(responseBody['errors']).toEqual([
{ message: `Missing required key 'query' in request body` },
- {
- message: `Invalid 'language' in request body 'fr'. Must be one of: en`,
- },
])
})
})