From 9d735706acd3b557dc6708ba9d4e98122ee2788a Mon Sep 17 00:00:00 2001 From: Evan Bonsignori Date: Mon, 2 Jun 2025 13:59:16 -0700 Subject: [PATCH 1/5] update CTA and add it to footer (#55865) Co-authored-by: Kevin Heis --- data/ui.yml | 6 +- src/fixtures/fixtures/data/ui.yml | 6 +- src/frame/components/DefaultLayout.tsx | 179 +++++++++--------- .../page-footer/SupportSection.module.scss | 25 +++ .../components/page-footer/SupportSection.tsx | 53 ++---- src/frame/components/page-header/Header.tsx | 7 +- .../context/SearchOverlayContext.tsx | 30 +++ .../components/input/AISearchCTAPopup.tsx | 150 +++++++++++---- .../components/input/SearchBarButton.tsx | 2 +- 9 files changed, 285 insertions(+), 173 deletions(-) create mode 100644 src/frame/components/page-footer/SupportSection.module.scss create mode 100644 src/search/components/context/SearchOverlayContext.tsx diff --git a/data/ui.yml b/data/ui.yml index 87dd1ea68685..97704600fc80 100644 --- a/data/ui.yml +++ b/data/ui.yml @@ -66,8 +66,10 @@ search: ai_title: There was an error loading Copilot. description: You can still use this field to search our docs. cta: - heading: New! Copilot for Docs - description: Ask your question in the search bar and get help in seconds. + heading: Get quick answers! + description: Ask Copilot your question. + dismiss: Dismiss + ask_copilot: Ask Copilot old_search: description: Enter a search term to find it in the GitHub Docs. placeholder: Search GitHub Docs diff --git a/src/fixtures/fixtures/data/ui.yml b/src/fixtures/fixtures/data/ui.yml index 87dd1ea68685..97704600fc80 100644 --- a/src/fixtures/fixtures/data/ui.yml +++ b/src/fixtures/fixtures/data/ui.yml @@ -66,8 +66,10 @@ search: ai_title: There was an error loading Copilot. description: You can still use this field to search our docs. cta: - heading: New! Copilot for Docs - description: Ask your question in the search bar and get help in seconds. + heading: Get quick answers! + description: Ask Copilot your question. + dismiss: Dismiss + ask_copilot: Ask Copilot old_search: description: Enter a search term to find it in the GitHub Docs. placeholder: Search GitHub Docs diff --git a/src/frame/components/DefaultLayout.tsx b/src/frame/components/DefaultLayout.tsx index ec22f51f7f35..f3a92cac2fd0 100644 --- a/src/frame/components/DefaultLayout.tsx +++ b/src/frame/components/DefaultLayout.tsx @@ -14,6 +14,7 @@ import { Breadcrumbs } from 'src/frame/components/page-header/Breadcrumbs' import { useLanguages } from 'src/languages/components/LanguagesContext' import { ClientSideLanguageRedirect } from './ClientSideLanguageRedirect' import { DomainNameEditProvider } from 'src/links/components/useEditableDomainContext' +import { SearchOverlayContextProvider } from '@/search/components/context/SearchOverlayContext' const MINIMAL_RENDER = Boolean(JSON.parse(process.env.MINIMAL_RENDER || 'false')) @@ -76,96 +77,98 @@ export const DefaultLayout = (props: Props) => { return ( - - {error === '404' ? ( - {t('oops')} - ) : (!isHomepageVersion && page.fullTitle) || - (currentPathWithoutLanguage.includes('enterprise-server') && page.fullTitle) ? ( - {page.fullTitle} - ) : null} - - {/* For Google and Bots */} - - {page.hidden && } - {Object.values(languages) - .filter((lang) => lang.code !== router.locale) - .map((variant) => { - return ( - - ) - })} - - {/* For local site search indexing */} - {page.topics.length > 0 && } - - {/* For analytics events */} - {router.locale && } - {currentVersion && } - {currentProduct && } - {relativePath && ( - - )} - {page.type && } - {page.documentType && } - {status && } - - {/* OpenGraph data */} - {page.fullTitle && ( - <> - - - - - - - )} - {/* Twitter Meta Tags */} - - - - - {page.introPlainText && } - - - - Skip to main content - -
- -
- {isHomepageVersion ? null : } - {/* Need to set an explicit height for sticky elements since we also - set overflow to auto */} -
-
- - - - {props.children} -
-
- - - + + {error === '404' ? ( + {t('oops')} + ) : (!isHomepageVersion && page.fullTitle) || + (currentPathWithoutLanguage.includes('enterprise-server') && page.fullTitle) ? ( + {page.fullTitle} + ) : null} + + {/* For Google and Bots */} + + {page.hidden && } + {Object.values(languages) + .filter((lang) => lang.code !== router.locale) + .map((variant) => { + return ( + + ) + })} + + {/* For local site search indexing */} + {page.topics.length > 0 && } + + {/* For analytics events */} + {router.locale && } + {currentVersion && } + {currentProduct && } + {relativePath && ( + -
+ )} + {page.type && } + {page.documentType && } + {status && } + + {/* OpenGraph data */} + {page.fullTitle && ( + <> + + + + + + + )} + {/* Twitter Meta Tags */} + + + + + {page.introPlainText && } + + + + Skip to main content + +
+ +
+ {isHomepageVersion ? null : } + {/* Need to set an explicit height for sticky elements since we also + set overflow to auto */} +
+
+ + + + {props.children} +
+
+ + + +
+
-
+ ) } diff --git a/src/frame/components/page-footer/SupportSection.module.scss b/src/frame/components/page-footer/SupportSection.module.scss new file mode 100644 index 000000000000..1bb704c2bc40 --- /dev/null +++ b/src/frame/components/page-footer/SupportSection.module.scss @@ -0,0 +1,25 @@ +.supportGrid { + display: grid; + gap: var(--base-size-4); + + // Mobile is 1 column + grid-template-columns: 1fr; + grid-column-gap: 1rem; + + grid-row-gap: 2rem; +} + +// Medium is 2 columns +@media (min-width: 768px) { + .supportGrid { + grid-template-columns: repeat(2, 1fr); + grid-row-gap: 1rem; + } +} + +// Large is 4 columns +@media (min-width: 1280px) { + .supportGrid { + grid-template-columns: minmax(18rem, 1fr) repeat(3, 1fr); + } +} diff --git a/src/frame/components/page-footer/SupportSection.tsx b/src/frame/components/page-footer/SupportSection.tsx index bfaf4e2fc64b..147e000cc5be 100644 --- a/src/frame/components/page-footer/SupportSection.tsx +++ b/src/frame/components/page-footer/SupportSection.tsx @@ -7,12 +7,17 @@ import { useMainContext } from 'src/frame/components/context/MainContext' import { useVersion } from 'src/versions/components/useVersion' import { useRouter } from 'next/router' import { useTranslation } from 'src/languages/components/useTranslation' +import { AISearchCTAPopup } from '@/search/components/input/AISearchCTAPopup' +import { useSearchOverlayContext } from '@/search/components/context/SearchOverlayContext' + +import styles from './SupportSection.module.scss' export const SupportSection = () => { const { currentVersion } = useVersion() const { relativePath, enterpriseServerReleases } = useMainContext() const router = useRouter() const { t } = useTranslation('footer') + const { setIsSearchOpen } = useSearchOverlayContext() const isDeprecated = enterpriseServerReleases.isOldestReleaseDeprecated && @@ -24,47 +29,25 @@ export const SupportSection = () => { const showSurvey = !isDeprecated && !isSitePolicyDocs const showContribution = !isDeprecated && !isEarlyAccess && isEnglish const showSupport = true - const totalCols = Number(showSurvey) + Number(showContribution) + Number(showSupport) + const showCopilotCTA = !isDeprecated && !isEarlyAccess && isEnglish return (

{t('support_heading')}

-
- {showSurvey && ( -
1 && 'col-lg-6', - totalCols > 2 && 'col-xl-3', - )} - > - -
- )} - {showContribution && ( -
1 && 'col-lg-6', - totalCols > 2 && 'col-xl-4', - totalCols > 2 && showSurvey && 'offset-xl-1', - )} - > - -
+ + {/* CSS Grid container */} +
1 && 'col-lg-6', - totalCols > 2 && 'col-xl-3', - totalCols > 2 && (showSurvey || showContribution) && 'offset-xl-1', - )} - > - -
+ > + {showCopilotCTA && ( + )} + {showSurvey && } + {showContribution && } + {showSupport && }
) diff --git a/src/frame/components/page-header/Header.tsx b/src/frame/components/page-header/Header.tsx index 73ae0756f47d..9d9d29e2ff61 100644 --- a/src/frame/components/page-header/Header.tsx +++ b/src/frame/components/page-header/Header.tsx @@ -20,10 +20,10 @@ import { HeaderSearchAndWidgets } from './HeaderSearchAndWidgets' import { useInnerWindowWidth } from './hooks/useInnerWindowWidth' import { EXPERIMENTS } from '@/events/components/experiments/experiments' import { useShouldShowExperiment } from '@/events/components/experiments/useShouldShowExperiment' -import { useQueryParam } from '@/frame/components/hooks/useQueryParam' import { useMultiQueryParams } from '@/search/components/hooks/useMultiQueryParams' import { SearchOverlayContainer } from '@/search/components/input/SearchOverlayContainer' import { useCTAPopoverContext } from '@/frame/components/context/CTAContext' +import { useSearchOverlayContext } from '@/search/components/context/SearchOverlayContext' import styles from './Header.module.scss' @@ -34,10 +34,6 @@ export const Header = () => { const { currentVersion } = useVersion() const { t } = useTranslation(['header']) const isRestPage = currentProduct && currentProduct.id === 'rest' - const { queryParam: isSearchOpen, setQueryParam: setIsSearchOpen } = useQueryParam( - 'search-overlay-open', - true, - ) const { params, updateParams } = useMultiQueryParams() const [scroll, setScroll] = useState(false) const [isSidebarOpen, setIsSidebarOpen] = useState(false) @@ -52,6 +48,7 @@ export const Header = () => { const returnFocusRef = useRef(null) const searchButtonRef = useRef(null) const { initializeCTA } = useCTAPopoverContext() + const { isSearchOpen, setIsSearchOpen } = useSearchOverlayContext() const { showExperiment: showNewSearch, experimentLoading: newSearchLoading } = useShouldShowExperiment(EXPERIMENTS.ai_search_experiment) diff --git a/src/search/components/context/SearchOverlayContext.tsx b/src/search/components/context/SearchOverlayContext.tsx new file mode 100644 index 000000000000..80f0bebecd9f --- /dev/null +++ b/src/search/components/context/SearchOverlayContext.tsx @@ -0,0 +1,30 @@ +// Context to manage the state of the SearchOverlay +import { createContext, useContext, PropsWithChildren } from 'react' +import { useQueryParam } from '@/frame/components/hooks/useQueryParam' + +type SearchOverlayState = { + isSearchOpen: boolean + setIsSearchOpen: (open: boolean) => void +} + +const SearchOverlayContext = createContext(undefined) + +export function SearchOverlayContextProvider({ children }: PropsWithChildren) { + const { queryParam: isSearchOpen, setQueryParam: setIsSearchOpen } = useQueryParam( + 'search-overlay-open', + true, + ) + + return ( + + {children} + + ) +} + +export const useSearchOverlayContext = () => { + const ctx = useContext(SearchOverlayContext) + if (!ctx) + throw new Error('useSearchOverlayContext must be used inside ') + return ctx +} diff --git a/src/search/components/input/AISearchCTAPopup.tsx b/src/search/components/input/AISearchCTAPopup.tsx index de05812fce93..8ddd1a69fb1a 100644 --- a/src/search/components/input/AISearchCTAPopup.tsx +++ b/src/search/components/input/AISearchCTAPopup.tsx @@ -1,19 +1,37 @@ import { useEffect, useRef } from 'react' -import { Text, Button, Heading, Popover, useOnEscapePress } from '@primer/react' +import { Text, Button, Heading, Popover, useOnEscapePress, Box } from '@primer/react' import { focusTrap } from '@primer/behaviors' import { useTranslation } from '@/languages/components/useTranslation' import { useMaxWidthBreakpoint, useMinWidthBreakpoint } from '../hooks/useBreakpoint' +import { useCTAPopoverContext } from '@/frame/components/context/CTAContext' let previouslyFocused: HTMLElement | null = null -export function AISearchCTAPopup({ isOpen, dismiss }: { isOpen: boolean; dismiss: () => void }) { +export function AISearchCTAPopup({ + isOpen, + dismiss, + setIsSearchOpen, + isDismissible = true, +}: { + isOpen: boolean + dismiss?: () => void + setIsSearchOpen: (value: boolean) => void + isDismissible?: boolean +}) { const { t } = useTranslation('search') + const { permanentDismiss } = useCTAPopoverContext() const isLargeOrUp = useMinWidthBreakpoint('large') const isTooSmallForCTA = useMaxWidthBreakpoint('293px') let overlayRef = useRef(null) let dismissButtonRef = useRef(null) + const openSearch = () => { + setIsSearchOpen(true) + // They engaged with the CTA, so let's not show this popup for them anymore + permanentDismiss() + } + // For a11y, focus trap the CTA and allow it to be closed with Escape useEffect(() => { if (isTooSmallForCTA) { @@ -32,7 +50,9 @@ export function AISearchCTAPopup({ isOpen, dismiss }: { isOpen: boolean; dismiss if (previouslyFocused) { previouslyFocused.focus() } - dismiss() + if (dismiss) { + dismiss() + } } useOnEscapePress(onDismiss) @@ -41,6 +61,92 @@ export function AISearchCTAPopup({ isOpen, dismiss }: { isOpen: boolean; dismiss return null } + const innerContent = ( + <> + The Copilot Icon in front of an explosion of color. + + {t('search.cta.heading')} + + + {t('search.cta.description')} + + + {isDismissible ? ( + + ) : null} + + + + ) + + // If not dismissible, it's not being used as a popover + if (!isDismissible) { + return ( + + {innerContent} + + ) + } + return ( - The Copilot Icon in front of an explosion of color. - - {t('search.cta.heading')} - - - {t('search.cta.description')} - - + {innerContent} ) diff --git a/src/search/components/input/SearchBarButton.tsx b/src/search/components/input/SearchBarButton.tsx index 411f4de0d954..eef75d1c29d9 100644 --- a/src/search/components/input/SearchBarButton.tsx +++ b/src/search/components/input/SearchBarButton.tsx @@ -50,7 +50,7 @@ export function SearchBarButton({ isSearchOpen, setIsSearchOpen, params, searchB {/* We don't want to show the input when overlay is open */} {!isSearchOpen ? ( <> - + {/* On mobile only the IconButton is shown */} Date: Mon, 2 Jun 2025 14:01:24 -0700 Subject: [PATCH 2/5] Attempt index fix round 3 (#55901) --- .../scripts/index/utils/indexing-elasticsearch-utils.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/search/scripts/index/utils/indexing-elasticsearch-utils.ts b/src/search/scripts/index/utils/indexing-elasticsearch-utils.ts index 1c5de5dfdd28..cf7fcc087cf8 100644 --- a/src/search/scripts/index/utils/indexing-elasticsearch-utils.ts +++ b/src/search/scripts/index/utils/indexing-elasticsearch-utils.ts @@ -55,10 +55,11 @@ export async function populateIndex( client.helpers.bulk({ datasource: records, onDocument: () => ({ index: { _index: indexAlias } }), - flushBytes: 10_000_000, // stop before breaker trips - concurrency: 2, // back-off a bit + flushBytes: 4 * 1024 * 1024, // 4MB - Prevents too large of a bulk request which results in a 429 from ES + concurrency: 2, refreshOnCompletion: true, timeout: '5m', + // We could use `retries` and `wait` here, but then we don't have as granular control over logging and when to retry }), { attempts, From 6d89bb94c4f4f8b753e45cba4c9192c953382e84 Mon Sep 17 00:00:00 2001 From: Justin Alex <1155821+jusuchin85@users.noreply.github.com> Date: Tue, 3 Jun 2025 08:17:12 +1000 Subject: [PATCH 3/5] [Improvement]: Add `Base URL` on EMU SCIM with Okta article, for customers using GitHub Cloud with Data Residency (#55872) --- .../configuring-scim-provisioning-with-okta.md | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/content/admin/managing-iam/provisioning-user-accounts-with-scim/configuring-scim-provisioning-with-okta.md b/content/admin/managing-iam/provisioning-user-accounts-with-scim/configuring-scim-provisioning-with-okta.md index 5890a7681df3..578abab86e35 100644 --- a/content/admin/managing-iam/provisioning-user-accounts-with-scim/configuring-scim-provisioning-with-okta.md +++ b/content/admin/managing-iam/provisioning-user-accounts-with-scim/configuring-scim-provisioning-with-okta.md @@ -120,7 +120,16 @@ Before starting this section, ensure you have followed steps **1 to 4** in [AUTO 1. Click **Configure API integration**. 1. In the "API Token" field, enter the {% data variables.product.pat_v1 %} belonging to the setup user. - {% data reusables.scim.import-groups-unsupported %} + {% data reusables.scim.import-groups-unsupported %} + + {% ifversion ghec %} + + > [!IMPORTANT] + > For an enterprise on {% data variables.enterprise.data_residency %} (GHE.com), please enter the following URL in the **Base URL** field: {% raw %}`https://api.{subdomain}.ghe.com/scim/v2/enterprises/{subdomain}`{% endraw %} (ensuring to replace {% raw %}`{subdomain}`{% endraw %} with your enterprise's subdomain). + > + > **For example**: if your enterprise's subdomain is {% raw %}`acme`{% endraw %}, the base URL would be {% raw %}`https://api.acme.ghe.com/scim/v2/enterprises/acme`{% endraw %}. + + {% endif %} 1. Click **Test API Credentials**. If the test is successful, a verification message will appear at the top of the screen. 1. To save the token, click **Save**. From d95a3c8ed1830e1f8d111193d7b7f46bea45bd0c Mon Sep 17 00:00:00 2001 From: Justin Alex <1155821+jusuchin85@users.noreply.github.com> Date: Tue, 3 Jun 2025 08:30:58 +1000 Subject: [PATCH 4/5] [Improvement]: Remove Confusing Statement about 2FA Codes for EMU Setup User (#55873) Co-authored-by: Joe Clark <31087804+jc-clark@users.noreply.github.com> --- .../getting-started-with-enterprise-managed-users.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/content/admin/managing-iam/understanding-iam-for-enterprises/getting-started-with-enterprise-managed-users.md b/content/admin/managing-iam/understanding-iam-for-enterprises/getting-started-with-enterprise-managed-users.md index 517d4d6dacd6..70b65a0ee210 100644 --- a/content/admin/managing-iam/understanding-iam-for-enterprises/getting-started-with-enterprise-managed-users.md +++ b/content/admin/managing-iam/understanding-iam-for-enterprises/getting-started-with-enterprise-managed-users.md @@ -39,7 +39,10 @@ Using an **incognito or private browsing window**: 1. Enable two-factor authentication (2FA), and save the recovery codes. See [AUTOTITLE](/authentication/securing-your-account-with-two-factor-authentication-2fa/configuring-two-factor-authentication). > [!WARNING] - > All subsequent login attempts for the setup user account will require a successful 2FA challenge response or the use of an enterprise recovery code to complete authentication. To avoid being locked out of your account, after enabling single sign-on, save your enterprise recovery codes. See [AUTOTITLE](/admin/managing-iam/managing-recovery-codes-for-your-enterprise/downloading-your-enterprise-accounts-single-sign-on-recovery-codes#downloading-codes-for-an-enterprise-with-enterprise-managed-users). + > All subsequent login attempts for the setup user account will require a successful 2FA challenge response. + + > [!IMPORTANT] + > If the enterprise account has enabled single sign-on and the setup user hasn’t enabled 2FA, they must use an enterprise recovery code to authenticate. To avoid being locked out of your account, after enabling single sign-on, save your enterprise recovery codes. For more information, see [AUTOTITLE](/admin/managing-iam/managing-recovery-codes-for-your-enterprise/downloading-your-enterprise-accounts-single-sign-on-recovery-codes#downloading-codes-for-an-enterprise-with-enterprise-managed-users) and the related [changelog in our {% data variables.product.prodname_blog %}](https://github.blog/changelog/2025-01-17-setup-user-for-emu-enterprises-requires-2fa-or-use-of-a-recovery-code/). {% data reusables.enterprise-accounts.emu-password-reset-session %} From 78c89d9b78be2ad9595c0ec2901879dec9d4205c Mon Sep 17 00:00:00 2001 From: Pallavi <96553709+pallsama@users.noreply.github.com> Date: Mon, 2 Jun 2025 16:35:45 -0700 Subject: [PATCH 5/5] GHES 3.17: Database transitions concurrently (#55524) Co-authored-by: Sophie <29382425+sophietheking@users.noreply.github.com> Co-authored-by: vgrl --- .../preparing-to-upgrade/overview-of-the-upgrade-process.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/admin/upgrading-your-instance/preparing-to-upgrade/overview-of-the-upgrade-process.md b/content/admin/upgrading-your-instance/preparing-to-upgrade/overview-of-the-upgrade-process.md index 620fdd2d62a4..29d21889f203 100644 --- a/content/admin/upgrading-your-instance/preparing-to-upgrade/overview-of-the-upgrade-process.md +++ b/content/admin/upgrading-your-instance/preparing-to-upgrade/overview-of-the-upgrade-process.md @@ -80,7 +80,7 @@ Check if you need to upgrade the following applications: > [!NOTE] > Hotpatches require a configuration run, which can cause a brief period of errors or unresponsiveness for some or all services on {% data variables.location.product_location %}. You are not required to enable maintenance mode during installation of a hotpatch, but doing so will guarantee that users see a maintenance page instead of errors or timeouts. See [AUTOTITLE](/admin/configuration/configuring-your-enterprise/enabling-and-scheduling-maintenance-mode). * Patch releases using an upgrade package typically require less than five minutes of downtime. - * Upgrading to a new feature release that include data migrations may cause a few hours of downtime, depending on storage performance and the amount of data that is migrated. During this time none of your users will be able to use the enterprise. + * Upgrading to a new feature release that includes data migrations may cause a few hours of downtime, depending on storage performance and the amount of data that is migrated. During this time none of your users will be able to use the enterprise.{% ifversion ghes > 3.16 %} You may notice that upgrades to a new feature release take less time. This is because selective database transitions will now run concurrently, with the number of concurrent workers defaulting to the number of CPU cores, up to a maximum of 16.{% endif %} ## Communicating your upgrade