Skip to content

Commit

Permalink
Merge pull request github#36242 from github/repo-sync
Browse files Browse the repository at this point in the history
Repo sync
  • Loading branch information
docs-bot authored Feb 11, 2025
2 parents 0a03e4f + 624bd0d commit 1fa5506
Show file tree
Hide file tree
Showing 10 changed files with 247 additions and 156 deletions.
5 changes: 4 additions & 1 deletion src/events/components/events.ts
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,10 @@ export function sendEvent<T extends EventType>({
os_preference: Cookies.get('osPreferred'),
code_display_preference: Cookies.get('annotate-mode'),

experiment_variation: getExperimentVariationForContext(getMetaContent('path-language')),
experiment_variation: getExperimentVariationForContext(
getMetaContent('path-language'),
getMetaContent('path-version'),
),

// Event grouping
event_group_key: eventGroupKey,
Expand Down
7 changes: 1 addition & 6 deletions src/events/components/experiments/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,19 +68,14 @@ In the code that displays the search bar, you can use the `shouldShowExperiment`
Example:

```typescript
import { useRouter } from 'next/router'
import { useShouldShowExperiment } from '@/events/components/experiments/useShouldShowExperiment'
import { EXPERIMENTS } from '@/events/components/experiments/experiments'
import { ClassicSearchBar } from "@/search/components/ClassicSearchBar.tsx"
import { NewSearchBar } from "@/search/components/NewSearchBar.tsx"

export function SearchBar() {
const router = useRouter()
// Users who were randomly placed in the `treatment` group will be shown the experiment
const { shouldShow: shouldShowNewSearch } = useShouldShowExperiment(
EXPERIMENTS.ai_search_experiment,
router.locale
)
const { shouldShow: shouldShowNewSearch } = useShouldShowExperiment(EXPERIMENTS.ai_search_experiment)

if (shouldShowNewSearch) {
return (
Expand Down
43 changes: 37 additions & 6 deletions src/events/components/experiments/experiment.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import murmur from 'imurmurhash'
import {
CONTROL_VARIATION,
EXPERIMENTS,
ExperimentNames,
TREATMENT_VARIATION,
getActiveExperiments,
Expand All @@ -12,6 +13,7 @@ let experimentsInitialized = false
export function shouldShowExperiment(
experimentKey: ExperimentNames | { key: ExperimentNames },
locale: string,
version: string,
isStaff: boolean,
) {
// Accept either EXPERIMENTS.<experiment_key> or EXPERIMENTS.<experiment_key>.key
Expand Down Expand Up @@ -40,8 +42,10 @@ export function shouldShowExperiment(
return controlGroup === TREATMENT_VARIATION
// Otherwise use the regular logic to determine if the user is in the treatment group
} else if (
experiment.limitToLanguages?.length &&
experiment.limitToLanguages.includes(locale)
(experiment.limitToLanguages?.length
? experiment.limitToLanguages.includes(locale)
: true) &&
(experiment.limitToVersions?.length ? experiment.limitToVersions.includes(version) : true)
) {
return (
getExperimentControlGroupFromSession(
Expand Down Expand Up @@ -98,8 +102,8 @@ export function getExperimentControlGroupFromSession(
return modHash < percentToGetExperiment ? TREATMENT_VARIATION : CONTROL_VARIATION
}

export function getExperimentVariationForContext(locale: string): string {
const experiments = getActiveExperiments(locale)
export function getExperimentVariationForContext(locale: string, version: string): string {
const experiments = getActiveExperiments(locale, version)
for (const experiment of experiments) {
if (experiment.includeVariationInContext) {
return getExperimentControlGroupFromSession(
Expand All @@ -113,11 +117,38 @@ export function getExperimentVariationForContext(locale: string): string {
return ''
}

export function initializeExperiments(locale: string) {
export function initializeExperiments(
locale: string,
currentVersion: string,
allVersions: { [key: string]: { version: string } },
) {
if (experimentsInitialized) return
experimentsInitialized = true

const experiments = getActiveExperiments(locale)
// Replace any occurrence of 'enterprise-server@latest' with the actual latest version
for (const [experimentKey, experiment] of Object.entries(EXPERIMENTS)) {
if (experiment.limitToVersions?.includes('enterprise-server@latest')) {
// Sort the versions in descending order so that the latest enterprise-server version is first
const latestEnterpriseServerVersion = Object.keys(allVersions)
.filter((version) => version.startsWith('enterprise-server@'))
.sort((a, b) => {
const aVersion = a.split('@')[1]
const bVersion = b.split('@')[1]
return Number(bVersion) - Number(aVersion)
})[0]
if (latestEnterpriseServerVersion) {
EXPERIMENTS[experimentKey as ExperimentNames].limitToVersions =
experiment.limitToVersions.map((version) =>
version.replace(
'enterprise-server@latest',
allVersions[latestEnterpriseServerVersion].version,
),
)
}
}
}

const experiments = getActiveExperiments(locale, currentVersion)

if (experiments.length && process.env.NODE_ENV === 'development') {
console.log(
Expand Down
38 changes: 32 additions & 6 deletions src/events/components/experiments/experiments.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ type Experiment = {
// Only one experiment's control group (variation) can be included in the context at a time
includeVariationInContext?: boolean
limitToLanguages?: string[]
limitToVersions?: string[]
alwaysShowForStaff: boolean
}

Expand All @@ -22,6 +23,11 @@ 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: false, // When set to true, staff will always see the experiment (determined by the `staffonly` cookie)
},
/* Add new experiments here, example:
Expand All @@ -31,17 +37,37 @@ export const EXPERIMENTS = {
percentOfUsersToGetExperiment: 10, // 10% of users will randomly get the experiment
includeVariationInContext: true, // All events will include the `experiment_variation` of the `example_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 the latest versions
alwaysShowForStaff: true, // When set to true, staff will always see the experiment (determined by the `staffonly` cookie)
}
*/
} as Record<ExperimentNames, Experiment>

export function getActiveExperiments(locale: string): Experiment[] {
export function getActiveExperiments(locale: string, version?: string): Experiment[] {
return Object.values(EXPERIMENTS).filter((experiment) => {
return (
experiment.isActive &&
(locale === 'all' ||
(experiment.limitToLanguages?.length ? experiment.limitToLanguages.includes(locale) : true))
)
if (locale === 'all') {
return true
}

let include = true
if (!experiment.isActive) {
include = false
}

// Only include experiment if it's supported for the current language
if (experiment.limitToLanguages?.length && !experiment.limitToLanguages.includes(locale)) {
include = false
}

// Only include experiment if it's supported for the current version
if (experiment.limitToVersions?.length && !experiment.limitToVersions.includes(version || '')) {
include = false
}

return include
})
}
20 changes: 14 additions & 6 deletions src/events/components/experiments/useShouldShowExperiment.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,30 @@
import { useEffect, useState } from 'react'
import { useRouter } from 'next/router'
import { shouldShowExperiment } from './experiment'
import { ExperimentNames } from './experiments'
import { getIsStaff } from '../dotcom-cookies'
import { useMainContext } from '@/frame/components/context/MainContext'

export function useShouldShowExperiment(
experimentKey: ExperimentNames | { key: ExperimentNames },
locale: string,
) {
export function useShouldShowExperiment(experimentKey: ExperimentNames | { key: ExperimentNames }) {
if (typeof experimentKey === 'object') {
experimentKey = experimentKey.key
}

const [showExperiment, setShowExperiment] = useState(false)
const router = useRouter()
const mainContext = useMainContext()

useEffect(() => {
const updateShouldShow = async () => {
const isStaff = await getIsStaff()
setShowExperiment(shouldShowExperiment(experimentKey, locale, isStaff))
setShowExperiment(
shouldShowExperiment(
experimentKey,
router.locale || '',
mainContext.currentVersion || '',
isStaff,
),
)
}

updateShouldShow()
Expand All @@ -27,7 +35,7 @@ export function useShouldShowExperiment(
return () => {
window.removeEventListener('controlGroupOverrideChanged', updateShouldShow)
}
}, [experimentKey])
}, [experimentKey, router.locale, mainContext.currentVersion])

return showExperiment
}
5 changes: 1 addition & 4 deletions src/frame/components/page-header/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,10 +51,7 @@ export const Header = () => {
const { width } = useInnerWindowWidth()
const returnFocusRef = useRef(null)

const showNewSearch = useShouldShowExperiment(
EXPERIMENTS.ai_search_experiment,
router.locale as string,
)
const showNewSearch = useShouldShowExperiment(EXPERIMENTS.ai_search_experiment)

useEffect(() => {
function onScroll() {
Expand Down
6 changes: 5 additions & 1 deletion src/frame/pages/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,11 @@ const MyApp = ({ Component, pageProps, languagesContext }: MyAppProps) => {

useEffect(() => {
initializeEvents()
initializeExperiments(router.locale as string)
initializeExperiments(
router.locale as string,
pageProps.mainContext.currentVersion,
pageProps.mainContext.allVersions,
)
}, [])

useEffect(() => {
Expand Down
11 changes: 10 additions & 1 deletion src/search/components/helpers/ai-search-links-json.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,16 @@ function extractMarkdownLinks(markdownResponse: string) {
urls.push(match[2])
}

return urls
// Filter out any invalid URLs
return urls.filter((url) => {
try {
new URL(url)
return true
// eslint-disable-next-line @typescript-eslint/no-unused-vars
} catch (e) {
return false
}
})
}

// Given a Docs URL, extract the product name
Expand Down
Loading

0 comments on commit 1fa5506

Please sign in to comment.