Skip to content

Commit d958a64

Browse files
authored
Merge branch 'main' into lucascosti/runner-latest-note
2 parents 527545b + becada8 commit d958a64

File tree

273 files changed

+1363
-349
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

273 files changed

+1363
-349
lines changed

.github/workflows/openapi-schema-check.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,9 @@ on:
2121
paths:
2222
- 'lib/rest/static/**'
2323
- 'script/rest/**/*.js'
24+
- 'script/rest/**/*.json'
2425
- 'package*.json'
26+
- 'lib/redirects/static/**/*.json'
2527

2628
permissions:
2729
contents: read

.github/workflows/optimize-images.yml

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
name: Optimize images
2+
3+
# **What it does**: Optimize images.
4+
# **Why we have it**: Reduce bandwidth needs.
5+
# **Who does it impact**: Docs engineering.
6+
7+
on:
8+
workflow_dispatch:
9+
schedule:
10+
- cron: '45 17 * * 2' # Run Tuesdays at 17:45 UTC / 9:45 PST
11+
12+
permissions:
13+
contents: write
14+
pull-requests: write
15+
16+
jobs:
17+
optimize-images:
18+
if: github.repository == 'github/docs-internal'
19+
runs-on: ubuntu-latest
20+
steps:
21+
- name: Check out repo
22+
uses: actions/checkout@ec3a7ce113134d7a93b817d10a8272cb61118579
23+
24+
- name: Install the Optipng package
25+
run: sudo apt-get update && sudo apt-get -y install optipng
26+
27+
- name: Run the Optipng package
28+
run: find . -name '*.png' -print0 | xargs -0 optipng -nx
29+
30+
- name: Make a branch, commit, push, and pull request
31+
run: |
32+
echo "If there's no changes, exit"
33+
if [[ `git status --porcelain` ]]
34+
then
35+
else
36+
exit 0
37+
fi
38+
39+
echo "Make a new branch"
40+
git checkout -b "optimize-images-$(date +'%Y%m%d%H%M%S')"
41+
42+
echo "Make a commit"
43+
git config user.name github-actions
44+
git config user.email github-actions@github.com
45+
git add "*.png"
46+
git commit --message="Optimize images"
47+
48+
echo "Push up changes"
49+
git push
50+
51+
echo "Open a pull request"
52+
gh pr create --title "Optimize images" --body "Optimize images"
53+
env:
54+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

.github/workflows/staging-deploy-pr.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ jobs:
7171
// the API for more info based on the originating workflow run
7272
const { BUILD_ACTIONS_RUN_ID } = process.env
7373
const { owner, repo } = context.repo
74-
const { data: run } = await github.rest.actions.getWorkflowRun({
74+
const { data: run } = await github.actions.getWorkflowRun({
7575
owner,
7676
repo,
7777
run_id: BUILD_ACTIONS_RUN_ID,
-7.23 KB
-48.8 KB
-69.6 KB
-26.4 KB
-8.84 KB
-14.7 KB
-39.2 KB
-42 KB
-43 KB
-34.4 KB
-45.7 KB
-14.2 KB
-49.1 KB
-1.89 KB
-33.9 KB
-9.78 KB
-10.2 KB
-1.61 KB
-6.99 KB
-5.08 KB
-10.2 KB
-1.57 KB
-8.15 KB
-8.68 KB

components/article/ArticlePage.tsx

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import { ToolPicker } from 'components/article/ToolPicker'
2222
const ClientSideRedirectExceptions = dynamic(() => import('./ClientsideRedirectExceptions'), {
2323
ssr: false,
2424
})
25+
const ClientSideHighlightJS = dynamic(() => import('./ClientSideHighlightJS'), { ssr: false })
2526

2627
// Mapping of a "normal" article to it's interactive counterpart
2728
const interactiveAlternatives: Record<string, { href: string }> = {
@@ -50,7 +51,7 @@ const interactiveAlternatives: Record<string, { href: string }> = {
5051
}
5152

5253
export const ArticlePage = () => {
53-
const router = useRouter()
54+
const { asPath } = useRouter()
5455
const {
5556
title,
5657
intro,
@@ -65,7 +66,7 @@ export const ArticlePage = () => {
6566
currentLearningTrack,
6667
} = useArticleContext()
6768
const { t } = useTranslation('pages')
68-
const currentPath = router.asPath.split('?')[0]
69+
const currentPath = asPath.split('?')[0]
6970

7071
const renderTocItem = (item: MiniTocItem) => {
7172
return (
@@ -111,6 +112,24 @@ export const ArticlePage = () => {
111112
}
112113
}, [])
113114

115+
// If the page contains `[data-highlight]` blocks, these pages need
116+
// syntax highlighting. But not every page needs it, so it's conditionally
117+
// lazy-loaded on the client.
118+
const [lazyLoadHighlightJS, setLazyLoadHighlightJS] = useState(false)
119+
useEffect(() => {
120+
// It doesn't need to use querySelector because all we care about is if
121+
// there is greater than zero of these in the DOM.
122+
// Note! This "core selector", which determines whether to bother
123+
// or not, needs to match what's used inside ClientSideHighlightJS.tsx
124+
if (document.querySelector('[data-highlight]')) {
125+
setLazyLoadHighlightJS(true)
126+
}
127+
128+
// Important to depend on the current path because the first page you
129+
// load, before any client-side navigation, might not need it, but the
130+
// consecutive one does.
131+
}, [asPath])
132+
114133
// Scrollable code blocks in our REST API docs and elsewhere aren't accessible
115134
// via keyboard navigation without setting tabindex="0". But we don't want to set
116135
// this attribute on every `<pre>` code block, only the ones where there are scroll
@@ -134,6 +153,10 @@ export const ArticlePage = () => {
134153
never render anything. It always just return null. */}
135154
{loadClientsideRedirectExceptions && <ClientSideRedirectExceptions />}
136155

156+
{/* Doesn't matter *where* this is included because it will
157+
never render anything. It always just return null. */}
158+
{lazyLoadHighlightJS && <ClientSideHighlightJS />}
159+
137160
<div className="container-xl px-3 px-md-6 my-4">
138161
<ArticleGridLayout
139162
topper={<ArticleTitle>{title}</ArticleTitle>}
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
import { useEffect } from 'react'
2+
import hljs from 'highlight.js/lib/core'
3+
import json from 'highlight.js/lib/languages/json'
4+
5+
// Add as needed. It's pretty cheap to add but please don't use
6+
// highlight.js import that loads all and everything.
7+
hljs.registerLanguage('json', json)
8+
const SUPPORTED_LANGUAGES = ['json']
9+
10+
// This is the selector we use for the first document.querySelectorAll()
11+
// to find the containers for `<code>` tags. Because it's s dataset
12+
// attribute, its value is expected to be the language.
13+
// E.g.
14+
//
15+
// <div class="whatever" data-highlight="javascript">
16+
// <p>other stuff</p>
17+
// <code>Ignored!</code>
18+
//
19+
// <pre>
20+
// <code>HIGHLIGHT THIS!</code>
21+
// </pre>
22+
//
23+
const CODE_ELEMENTS_PARENT_SELECTOR = '[data-highlight]'
24+
const CODE_SELECTOR = 'pre code'
25+
26+
export default function ClientSideHighlightJS() {
27+
useEffect(() => {
28+
// Hi Internet Explorer!
29+
// https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API#browser_compatibility
30+
// If intersectionObserver isn't supported, let's not bother doing
31+
// anything. If your browser is that behind, syntax highlight is probably
32+
// your least concern.
33+
if (!window.IntersectionObserver) return
34+
35+
const intersectionObserver = new IntersectionObserver((entries) => {
36+
for (const entry of entries) {
37+
if (entry.isIntersecting) {
38+
const element = entry.target as HTMLElement
39+
if (!element.classList.contains('hljs')) {
40+
hljs.highlightElement(element)
41+
}
42+
}
43+
}
44+
})
45+
for (const parent of Array.from(
46+
document.querySelectorAll<HTMLElement>(CODE_ELEMENTS_PARENT_SELECTOR)
47+
)) {
48+
const language = parent.dataset.highlight || 'json'
49+
if (!SUPPORTED_LANGUAGES.includes(language)) {
50+
if (process.env.NODE_ENV === 'development') {
51+
console.warn(
52+
`For highlighting, only ${SUPPORTED_LANGUAGES} is supported. Not '${language}'.`
53+
)
54+
}
55+
continue
56+
}
57+
for (const element of Array.from(parent.querySelectorAll<HTMLElement>(CODE_SELECTOR))) {
58+
element.classList.add(`language-${language}`)
59+
intersectionObserver.observe(element)
60+
}
61+
}
62+
}, [])
63+
64+
return null
65+
}

components/article/ClientsideRedirectExceptions.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
import { useEffect } from 'react'
22
import { useRouter } from 'next/router'
33

4-
import overrides from '../../lib/redirects/static/rest-api-redirect-exceptions.json'
5-
const overrideRedirects: Record<string, string> = overrides
4+
import restApiOverrides from '../../lib/redirects/static/client-side-rest-api-redirects.json'
5+
import productOverrides from '../../lib/redirects/static/client-side-product-redirects.json'
6+
const overrideRedirects: Record<string, string> = { ...restApiOverrides, ...productOverrides }
67

78
export default function ClientSideRedirectExceptions() {
89
const router = useRouter()

components/context/MainContext.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ type VersionItem = {
2525
versionTitle: string
2626
currentRelease: string
2727
latestVersion: string
28+
shortName: string
2829
}
2930

3031
export type ProductTreeNode = {

components/guides/LearningTrack.tsx

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ export const LearningTrack = ({ track }: Props) => {
2525
<div data-testid="learning-track" className="my-3 px-4 col-12 col-md-6">
2626
<div className="Box d-flex flex-column">
2727
<div className="Box-header color-bg-subtle p-4 d-flex flex-1 flex-items-start flex-wrap">
28-
<div className="d-flex flex-auto flex-items-start col-8 col-md-12 col-xl-8">
28+
<div className="d-flex flex-auto flex-items-start col-7 col-md-7 col-xl-7">
2929
<div className="my-xl-0 mr-xl-3">
3030
<h3 id={slug} className={cx('mb-3 color-text f3 text-semibold', styles.hashAnchor)}>
3131
<a className="color-unset" href={`#${slug}`}>
@@ -39,12 +39,11 @@ export const LearningTrack = ({ track }: Props) => {
3939
</div>
4040
<a
4141
className="d-inline-flex btn no-wrap mt-3 mt-md-0 flex-items-center flex-justify-center"
42-
role="button"
4342
href={`${track?.guides && track?.guides[0].href}?learn=${
4443
track?.trackName
4544
}&learnProduct=${track?.trackProduct}`}
4645
>
47-
<span>{t('start')}</span>
46+
<span>{t('start_path')}</span>
4847
<ArrowRightIcon size={20} className="ml-2" />
4948
</a>
5049
</div>

components/landing/ProductReleases.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ export function ProductReleases() {
3030
<p className="mt-2 mb-4 color-fg-muted">
3131
<ListUnorderedIcon />{' '}
3232
<Link
33+
className="text-bold"
3334
href={`/${router.locale}/${releaseVersion}/admin/release-notes#${latestPatch.version}`}
3435
>
3536
{t('release_notes_for')} {latestPatch.version}
@@ -39,20 +40,24 @@ export function ProductReleases() {
3940
<p className="mt-2 mb-4 color-fg-muted">
4041
<ArrowUpIcon /> {t('upgrade_from')}{' '}
4142
<Link
43+
className="text-bold"
4244
href={`/${router.locale}/${firstPreviousVersion}/admin/enterprise-management/upgrading-github-enterprise-server`}
4345
>
4446
{release.firstPreviousRelease}
4547
</Link>{' '}
4648
or{' '}
4749
<Link
50+
className="text-bold"
4851
href={`/${router.locale}/${secondPreviousVersion}/admin/enterprise-management/upgrading-github-enterprise-server`}
4952
>
5053
{release.secondPreviousRelease}
5154
</Link>
5255
</p>
5356
<p className="mt-2 mb-4 color-fg-muted">
5457
<FileIcon />{' '}
55-
<Link href={`/${router.locale}/${releaseVersion}`}>{t('browse_all_docs')}</Link>
58+
<Link className="text-bold" href={`/${router.locale}/${releaseVersion}`}>
59+
{t('browse_all_docs')}
60+
</Link>
5661
</p>
5762
</div>
5863
</div>

components/page-header/Header.tsx

Lines changed: 44 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { useEffect, useState } from 'react'
22
import cx from 'classnames'
33
import { useRouter } from 'next/router'
44
import { MarkGithubIcon, ThreeBarsIcon, XIcon } from '@primer/octicons-react'
5+
import { useVersion } from 'components/hooks/useVersion'
56

67
import { Link } from 'components/Link'
78
import { useMainContext } from 'components/context/MainContext'
@@ -16,12 +17,20 @@ import styles from './Header.module.scss'
1617

1718
export const Header = () => {
1819
const router = useRouter()
19-
const { relativePath, error } = useMainContext()
20+
const { relativePath, error, allVersions } = useMainContext()
21+
const { currentVersion } = useVersion()
2022
const { t } = useTranslation(['header', 'homepage'])
2123
const [isMenuOpen, setIsMenuOpen] = useState(
2224
router.pathname !== '/' && router.query.query && true
2325
)
2426
const [scroll, setScroll] = useState(false)
27+
const [signupCTAVisible, setSignupCTAVisible] = useState(false)
28+
29+
useEffect(() => {
30+
const allowedVersions: RegExp = /ghec|fpt/
31+
const canShowSignupCTA = allowedVersions.test(allVersions[currentVersion].shortName)
32+
setSignupCTAVisible(canShowSignupCTA)
33+
}, [currentVersion])
2534

2635
useEffect(() => {
2736
function onScroll() {
@@ -68,19 +77,31 @@ export const Header = () => {
6877
>
6978
<Breadcrumbs />
7079
</div>
80+
<div className="d-flex flex-items-center">
81+
<div className="mr-2">
82+
<VersionPicker />
83+
</div>
7184

72-
<div className="mr-2">
73-
<VersionPicker />
74-
</div>
75-
76-
<LanguagePicker />
85+
<LanguagePicker />
7786

78-
{/* <!-- GitHub.com homepage and 404 page has a stylized search; Enterprise homepages do not --> */}
79-
{relativePath !== 'index.md' && error !== '404' && (
80-
<div className="d-inline-block ml-3">
81-
<Search iconSize={16} isHeaderSearch={true} />
82-
</div>
83-
)}
87+
{signupCTAVisible && (
88+
<a
89+
href="https://github.com/signup?ref_cta=Sign+up&ref_loc=docs+header&ref_page=docs"
90+
target="_blank"
91+
rel="noopener"
92+
className="ml-3 btn color-fg-muted"
93+
>
94+
{t`sign_up_cta`}
95+
</a>
96+
)}
97+
98+
{/* <!-- GitHub.com homepage and 404 page has a stylized search; Enterprise homepages do not --> */}
99+
{relativePath !== 'index.md' && error !== '404' && (
100+
<div className="d-inline-block ml-3">
101+
<Search iconSize={16} isHeaderSearch={true} />
102+
</div>
103+
)}
104+
</div>
84105
</div>
85106

86107
{/* mobile header */}
@@ -128,10 +149,20 @@ export const Header = () => {
128149

129150
<div className="border-top my-2" />
130151
<LanguagePicker variant="inline" />
152+
{signupCTAVisible && (
153+
<a
154+
href="https://github.com/signup?ref_cta=Sign+up&ref_loc=docs+header&ref_page=docs"
155+
target="_blank"
156+
rel="noopener"
157+
className="mt-3 py-2 btn color-fg-muted d-block"
158+
>
159+
{t`sign_up_cta`}
160+
</a>
161+
)}
131162

132163
{/* <!-- GitHub.com homepage and 404 page has a stylized search; Enterprise homepages do not --> */}
133164
{relativePath !== 'index.md' && error !== '404' && (
134-
<div className="my-2 pt-3">
165+
<div className="my-2 pt-2">
135166
<Search iconSize={16} isMobileSearch={true} />
136167
</div>
137168
)}

components/ui/MarkdownContent/MarkdownContent.module.scss

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@
44
@import "./stylesheets/table.scss";
55

66
.markdownBody {
7+
a {
8+
text-decoration: underline;
9+
}
10+
711
summary {
812
outline: none;
913

@@ -24,6 +28,13 @@
2428
}
2529
}
2630

31+
/* For REST pages which have Parameters and Code Samples h4 headings that are also links. */
32+
h4 {
33+
a {
34+
text-decoration: none;
35+
}
36+
}
37+
2738
h1,
2839
h2,
2940
h3,

0 commit comments

Comments
 (0)