1
0
mirror of synced 2025-12-22 11:26:57 -05:00

Merge branch 'main' into patch-1

This commit is contained in:
Janice
2021-06-22 13:13:19 -07:00
committed by GitHub
88 changed files with 943 additions and 495 deletions

View File

@@ -41,5 +41,5 @@ jobs:
- name: Install dependencies - name: Install dependencies
run: npm ci --include=optional run: npm ci --include=optional
- name: Run brower-test - name: Run browser-test
run: npm run browser-test run: npm run browser-test

View File

@@ -48,7 +48,7 @@ jobs:
owner: owner, owner: owner,
repo: originalRepo, repo: originalRepo,
issue_number: issueNo, issue_number: issueNo,
body: `👋 Moving forward, we're asking that folks create all new Docs issues in the [${process.env.TEAM_ENGINEERING_REPO}](${process.env.TEAM_ENGINEERING_REPO}) repo and all new content issues in [${process.env.TEAM_CONTENT_REPO}](${process.env.TEAM_CONTENT_REPO}). We transferred it for you!` body: `👋 You opened this issue in `${context.repo.repo}`. Moving forward, we're asking that folks create new issues in the following repositories instead:\n- For issues with the docs site, please submit to the [${process.env.TEAM_ENGINEERING_REPO}](/${owner}/${process.env.TEAM_ENGINEERING_REPO}) repo.\n- For all new content issues, please submit to the [${process.env.TEAM_CONTENT_REPO}](/${owner}/${process.env.TEAM_CONTENT_REPO}) repo.\n\nWe will transfer this issue for you!`
}) })
// Transfer the issue to the correct repo // Transfer the issue to the correct repo

View File

@@ -4,7 +4,7 @@ import { useMainContext } from 'components/context/MainContext'
const { NODE_ENV } = process.env const { NODE_ENV } = process.env
const enableNextLinks = false const enableNextLinks = true
type Props = { locale?: string } & ComponentProps<'a'> type Props = { locale?: string } & ComponentProps<'a'>
export function Link(props: Props) { export function Link(props: Props) {

View File

@@ -10,7 +10,7 @@ import { useVersion } from './hooks/useVersion'
export const SidebarNav = () => { export const SidebarNav = () => {
const router = useRouter() const router = useRouter()
const { error, relativePath, isHomepageVersion } = useMainContext() const { error, relativePath } = useMainContext()
const { t } = useTranslation('header') const { t } = useTranslation('header')
return ( return (
@@ -39,7 +39,7 @@ export const SidebarNav = () => {
<nav> <nav>
{error === '404' || relativePath === 'index.md' ? ( {error === '404' || relativePath === 'index.md' ? (
<ul className="sidebar-products mt-4"> <ul className="sidebar-products mt-4">
{!isHomepageVersion && <AllProductsLink />} {<AllProductsLink />}
<SidebarHomepage /> <SidebarHomepage />
</ul> </ul>
) : ( ) : (
@@ -66,12 +66,12 @@ export const SidebarNav = () => {
const SidebarHomepage = () => { const SidebarHomepage = () => {
const router = useRouter() const router = useRouter()
const { currentVersion } = useVersion() const { currentVersion } = useVersion()
const { activeProducts, isHomepageVersion } = useMainContext() const { activeProducts } = useMainContext()
return ( return (
<> <>
{activeProducts.map((product) => { {activeProducts.map((product) => {
if (!product.versions?.includes(currentVersion) && !isHomepageVersion) { if (!product.versions?.includes(currentVersion) && !product.external) {
return null return null
} }

View File

@@ -128,7 +128,9 @@ export const Survey = () => {
</> </>
)} )}
{state === ViewState.END && <p className="color-text-secondary f6" data-testid="survey-end">{t`feedback`}</p>} {state === ViewState.END && (
<p className="color-text-secondary f6" data-testid="survey-end">{t`feedback`}</p>
)}
</form> </form>
) )
} }

View File

@@ -31,10 +31,6 @@ export const ArticleVersionPicker = () => {
</summary> </summary>
<Dropdown.Menu direction="sw"> <Dropdown.Menu direction="sw">
{(page.permalinks || []).map((permalink) => { {(page.permalinks || []).map((permalink) => {
if (permalink.pageVersion === 'homepage') {
return null
}
return ( return (
<Dropdown.Item key={permalink.href}> <Dropdown.Item key={permalink.href}>
<Link href={permalink.href}>{permalink.pageVersionTitle}</Link> <Link href={permalink.href}>{permalink.pageVersionTitle}</Link>

View File

@@ -115,7 +115,7 @@ export const getMainContextFromRequest = (req: any): MainContextT => {
activeProducts: req.context.activeProducts, activeProducts: req.context.activeProducts,
currentProduct: req.context.productMap[req.context.currentProduct] || null, currentProduct: req.context.productMap[req.context.currentProduct] || null,
currentLayoutName: req.context.currentLayoutName, currentLayoutName: req.context.currentLayoutName,
isHomepageVersion: req.context.currentVersion === 'homepage', isHomepageVersion: req.context.page?.documentType === 'homepage',
error: req.context.error ? req.context.error.toString() : '', error: req.context.error ? req.context.error.toString() : '',
data: { data: {
ui: req.context.site.data.ui, ui: req.context.site.data.ui,

View File

@@ -62,7 +62,7 @@ export const getProductSubLandingContextFromRequest = (req: any): ProductSubLand
includeGuides: (page.includeGuides || []).map((guide: any) => { includeGuides: (page.includeGuides || []).map((guide: any) => {
return { return {
...pick(guide, ['href', 'title', 'intro', 'topics']), ...pick(guide, ['href', 'title', 'intro', 'topics']),
type: guide.type || '' type: guide.type || '',
} }
}), }),
} }

View File

@@ -6,7 +6,6 @@ import { ChevronDownIcon } from '@primer/octicons-react'
import { Link } from 'components/Link' import { Link } from 'components/Link'
import { useMainContext } from 'components/context/MainContext' import { useMainContext } from 'components/context/MainContext'
import { useVersion } from 'components/hooks/useVersion' import { useVersion } from 'components/hooks/useVersion'
import { useTranslation } from 'components/hooks/useTranslation'
type Props = { type Props = {
variant?: 'inline' variant?: 'inline'
@@ -15,14 +14,13 @@ export const HomepageVersionPicker = ({ variant }: Props) => {
const router = useRouter() const router = useRouter()
const { currentVersion } = useVersion() const { currentVersion } = useVersion()
const { getDetailsProps } = useDetails({}) const { getDetailsProps } = useDetails({})
const { allVersions, page, enterpriseServerVersions, isHomepageVersion } = useMainContext() const { allVersions, page, enterpriseServerVersions } = useMainContext()
const { t } = useTranslation('homepage')
if (page.permalinks && page.permalinks.length <= 1) { if (page.permalinks && page.permalinks.length <= 1) {
return null return null
} }
const label = isHomepageVersion ? t('version_picker') : allVersions[currentVersion].versionTitle const label = allVersions[currentVersion].versionTitle
if (variant === 'inline') { if (variant === 'inline') {
return ( return (
@@ -35,9 +33,6 @@ export const HomepageVersionPicker = ({ variant }: Props) => {
</summary> </summary>
<div> <div>
{(page.permalinks || []).map((permalink) => { {(page.permalinks || []).map((permalink) => {
if (permalink.pageVersion === 'homepage') {
return null
}
return ( return (
<Link <Link
key={permalink.href} key={permalink.href}
@@ -78,10 +73,6 @@ export const HomepageVersionPicker = ({ variant }: Props) => {
</summary> </summary>
<Dropdown.Menu direction="sw"> <Dropdown.Menu direction="sw">
{(page.permalinks || []).map((permalink) => { {(page.permalinks || []).map((permalink) => {
if (permalink.pageVersion === 'homepage') {
return null
}
return ( return (
<Dropdown.Item key={permalink.href}> <Dropdown.Item key={permalink.href}>
<Link href={permalink.href}>{permalink.pageVersionTitle}</Link> <Link href={permalink.href}>{permalink.pageVersionTitle}</Link>

View File

@@ -10,7 +10,9 @@ export const ArticleCard = ({ card, typeLabel }: Props) => {
<div data-testid="article-card" className="d-flex col-12 col-md-4 pr-0 pr-md-6 pr-lg-8"> <div data-testid="article-card" className="d-flex col-12 col-md-4 pr-0 pr-md-6 pr-lg-8">
<a className="no-underline d-flex flex-column py-3 border-bottom" href={card.href}> <a className="no-underline d-flex flex-column py-3 border-bottom" href={card.href}>
<h4 className="h4 color-text-primary mb-1">{card.title}</h4> <h4 className="h4 color-text-primary mb-1">{card.title}</h4>
<div className="h6 text-uppercase" data-testid="article-card-type">{typeLabel}</div> <div className="h6 text-uppercase" data-testid="article-card-type">
{typeLabel}
</div>
<p className="color-text-secondary my-3">{card.intro}</p> <p className="color-text-secondary my-3">{card.intro}</p>
{card.topics.length > 0 && ( {card.topics.length > 0 && (
<div> <div>

View File

@@ -9,7 +9,7 @@ export const SubLandingHero = () => {
const { t } = useTranslation('product_sublanding') const { t } = useTranslation('product_sublanding')
const guideItems = featuredTrack?.guides?.map((guide) => ( const guideItems = featuredTrack?.guides?.map((guide) => (
<li className="px-2 d-flex flex-shrink-0"> <li className="px-2 d-flex flex-shrink-0" key={guide.href}>
<Link <Link
href={`${guide.href}?learn=${featuredTrack.trackName}`} href={`${guide.href}?learn=${featuredTrack.trackName}`}
className="d-inline-block Box p-5 color-bg-primary color-border-primary no-underline" className="d-inline-block Box p-5 color-bg-primary color-border-primary no-underline"

View File

@@ -39,8 +39,9 @@ includeGuides:
- /code-security/secure-coding/integrating-with-code-scanning/sarif-support-for-code-scanning - /code-security/secure-coding/integrating-with-code-scanning/sarif-support-for-code-scanning
- /code-security/secure-coding/integrating-with-code-scanning/uploading-a-sarif-file-to-github - /code-security/secure-coding/integrating-with-code-scanning/uploading-a-sarif-file-to-github
- /code-security/secure-coding/using-codeql-code-scanning-with-your-existing-ci-system/about-codeql-code-scanning-in-your-ci-system - /code-security/secure-coding/using-codeql-code-scanning-with-your-existing-ci-system/about-codeql-code-scanning-in-your-ci-system
- /code-security/secure-coding/using-codeql-code-scanning-with-your-existing-ci-system/configuring-codeql-cli-in-your-ci-system
- /code-security/secure-coding/using-codeql-code-scanning-with-your-existing-ci-system/configuring-codeql-runner-in-your-ci-system - /code-security/secure-coding/using-codeql-code-scanning-with-your-existing-ci-system/configuring-codeql-runner-in-your-ci-system
- /code-security/secure-coding/using-codeql-code-scanning-with-your-existing-ci-system/running-codeql-cli-in-your-ci-system - /code-security/secure-coding/using-codeql-code-scanning-with-your-existing-ci-system/installing-codeql-cli-in-your-ci-system
- /code-security/secure-coding/using-codeql-code-scanning-with-your-existing-ci-system/running-codeql-runner-in-your-ci-system - /code-security/secure-coding/using-codeql-code-scanning-with-your-existing-ci-system/running-codeql-runner-in-your-ci-system
- /code-security/secure-coding/using-codeql-code-scanning-with-your-existing-ci-system/troubleshooting-codeql-runner-in-your-ci-system - /code-security/secure-coding/using-codeql-code-scanning-with-your-existing-ci-system/troubleshooting-codeql-runner-in-your-ci-system
- /code-security/security-advisories/about-coordinated-disclosure-of-security-vulnerabilities - /code-security/security-advisories/about-coordinated-disclosure-of-security-vulnerabilities

View File

@@ -29,7 +29,7 @@ topics:
If you're setting up {% data variables.product.prodname_code_scanning %} for a compiled language, and you're building the code in a containerized environment, the analysis may fail with the error message "No source code was seen during the build." This indicates that {% data variables.product.prodname_codeql %} was unable to monitor your code as it was compiled. If you're setting up {% data variables.product.prodname_code_scanning %} for a compiled language, and you're building the code in a containerized environment, the analysis may fail with the error message "No source code was seen during the build." This indicates that {% data variables.product.prodname_codeql %} was unable to monitor your code as it was compiled.
{% if currentVersion == "free-pro-team@latest" or currentVersion ver_gt "enterprise-server@3.0" or currentVersion == "github-ae@next" %} {% if currentVersion == "free-pro-team@latest" or currentVersion ver_gt "enterprise-server@3.0" or currentVersion == "github-ae@next" %}
You must run {% data variables.product.prodname_codeql %} inside the container in which you build your code. This applies whether you are using the {% data variables.product.prodname_codeql_cli %}, the {% data variables.product.prodname_codeql_runner %}, or {% data variables.product.prodname_actions %}. For the {% data variables.product.prodname_codeql_cli %} or the {% data variables.product.prodname_codeql_runner %}, see "[Running {% data variables.product.prodname_codeql_cli %} in your CI system](/code-security/secure-coding/running-codeql-cli-in-your-ci-system)" or "[Running {% data variables.product.prodname_codeql_runner %} in your CI system](/code-security/secure-coding/running-codeql-runner-in-your-ci-system)" for more information. If you're using {% data variables.product.prodname_actions %}, configure your workflow to run all the actions in the same container. For more information, see "[Example workflow](#example-workflow)." You must run {% data variables.product.prodname_codeql %} inside the container in which you build your code. This applies whether you are using the {% data variables.product.prodname_codeql_cli %}, the {% data variables.product.prodname_codeql_runner %}, or {% data variables.product.prodname_actions %}. For the {% data variables.product.prodname_codeql_cli %} or the {% data variables.product.prodname_codeql_runner %}, see "[Installing {% data variables.product.prodname_codeql_cli %} in your CI system](/code-security/secure-coding/using-codeql-code-scanning-with-your-existing-ci-system/installing-codeql-cli-in-your-ci-system)" or "[Running {% data variables.product.prodname_codeql_runner %} in your CI system](/code-security/secure-coding/running-codeql-runner-in-your-ci-system)" for more information. If you're using {% data variables.product.prodname_actions %}, configure your workflow to run all the actions in the same container. For more information, see "[Example workflow](#example-workflow)."
{% else %} {% else %}
You must run {% data variables.product.prodname_codeql %} inside the container in which you build your code. This applies whether you are using the {% data variables.product.prodname_codeql_runner %} or {% data variables.product.prodname_actions %}. For the {% data variables.product.prodname_codeql_runner %}, see "[Running {% data variables.product.prodname_codeql_runner %} in your CI system](/code-security/secure-coding/running-codeql-runner-in-your-ci-system)" for more information. If you're using {% data variables.product.prodname_actions %}, configure your workflow to run all the actions in the same container. For more information, see "[Example workflow](#example-workflow)." You must run {% data variables.product.prodname_codeql %} inside the container in which you build your code. This applies whether you are using the {% data variables.product.prodname_codeql_runner %} or {% data variables.product.prodname_actions %}. For the {% data variables.product.prodname_codeql_runner %}, see "[Running {% data variables.product.prodname_codeql_runner %} in your CI system](/code-security/secure-coding/running-codeql-runner-in-your-ci-system)" for more information. If you're using {% data variables.product.prodname_actions %}, configure your workflow to run all the actions in the same container. For more information, see "[Example workflow](#example-workflow)."
{% endif %} {% endif %}

View File

@@ -32,7 +32,7 @@ To upload a SARIF file from a third-party static code analysis engine, you'll ne
If you're using {% data variables.product.prodname_actions %} with the {% data variables.product.prodname_codeql_workflow %} or using the {% data variables.product.prodname_codeql_runner %}, then the {% data variables.product.prodname_code_scanning %} results will automatically use the supported subset of SARIF 2.1.0. For more information, see "[Setting up {% data variables.product.prodname_code_scanning %} for a repository](/code-security/secure-coding/setting-up-code-scanning-for-a-repository)" or "[Running {% data variables.product.prodname_codeql_runner %} in your CI system](/code-security/secure-coding/running-codeql-runner-in-your-ci-system)." If you're using {% data variables.product.prodname_actions %} with the {% data variables.product.prodname_codeql_workflow %} or using the {% data variables.product.prodname_codeql_runner %}, then the {% data variables.product.prodname_code_scanning %} results will automatically use the supported subset of SARIF 2.1.0. For more information, see "[Setting up {% data variables.product.prodname_code_scanning %} for a repository](/code-security/secure-coding/setting-up-code-scanning-for-a-repository)" or "[Running {% data variables.product.prodname_codeql_runner %} in your CI system](/code-security/secure-coding/running-codeql-runner-in-your-ci-system)."
{% if currentVersion == "free-pro-team@latest" or currentVersion ver_gt "enterprise-server@3.0" or currentVersion == "github-ae@next" %} {% if currentVersion == "free-pro-team@latest" or currentVersion ver_gt "enterprise-server@3.0" or currentVersion == "github-ae@next" %}
If you're using the {% data variables.product.prodname_codeql_cli %}, then you can specify the version of SARIF to use. For more information, see "[Running {% data variables.product.prodname_codeql_cli %} in your CI system](/code-security/secure-coding/running-codeql-cli-in-your-ci-system#uploading-results-to-github)."{% endif %} If you're using the {% data variables.product.prodname_codeql_cli %}, then you can specify the version of SARIF to use. For more information, see "[Configuring {% data variables.product.prodname_codeql_cli %} in your CI system](/code-security/secure-coding/using-codeql-code-scanning-with-your-existing-ci-system/configuring-codeql-cli-in-your-ci-system#analyzing-a-codeql-database)."{% endif %}
{% if currentVersion == "free-pro-team@latest" %} {% if currentVersion == "free-pro-team@latest" %}
You can upload multiple SARIF files for the same tool and commit, and analyze each file using {% data variables.product.prodname_code_scanning %}. You can indicate a "category" for each analysis by specifying a `runAutomationDetails.id` in each file. Only SARIF files with the same category will overwrite each other. For more information about this property, see [`runAutomationDetails` object](#runautomationdetails-object) below. You can upload multiple SARIF files for the same tool and commit, and analyze each file using {% data variables.product.prodname_code_scanning %}. You can indicate a "category" for each analysis by specifying a `runAutomationDetails.id` in each file. Only SARIF files with the same category will overwrite each other. For more information about this property, see [`runAutomationDetails` object](#runautomationdetails-object) below.

View File

@@ -37,7 +37,7 @@ You can upload the results using {% data variables.product.prodname_actions %},
- {% data variables.product.prodname_actions %} to run the {% data variables.product.prodname_codeql %} action, there is no further action required. The {% data variables.product.prodname_codeql %} action uploads the SARIF file automatically when it completes analysis. - {% data variables.product.prodname_actions %} to run the {% data variables.product.prodname_codeql %} action, there is no further action required. The {% data variables.product.prodname_codeql %} action uploads the SARIF file automatically when it completes analysis.
- {% data variables.product.prodname_actions %} to run a SARIF-compatible analysis tool, you could update the workflow to include a final step that uploads the results (see below). {% if currentVersion == "free-pro-team@latest" or currentVersion ver_gt "enterprise-server@3.0" or currentVersion == "github-ae@next" %} - {% data variables.product.prodname_actions %} to run a SARIF-compatible analysis tool, you could update the workflow to include a final step that uploads the results (see below). {% if currentVersion == "free-pro-team@latest" or currentVersion ver_gt "enterprise-server@3.0" or currentVersion == "github-ae@next" %}
- The {% data variables.product.prodname_codeql_cli %} to run {% data variables.product.prodname_code_scanning %} in your CI system, you can use the CLI to upload results to {% data variables.product.prodname_dotcom %} (for more information, see "[Running {% data variables.product.prodname_codeql_cli %} in your CI system](/code-security/secure-coding/running-codeql-cli-in-your-ci-system)").{% endif %} - The {% data variables.product.prodname_codeql_cli %} to run {% data variables.product.prodname_code_scanning %} in your CI system, you can use the CLI to upload results to {% data variables.product.prodname_dotcom %} (for more information, see "[Installing {% data variables.product.prodname_codeql_cli %} in your CI system](/code-security/secure-coding/using-codeql-code-scanning-with-your-existing-ci-system/installing-codeql-cli-in-your-ci-system)").{% endif %}
- The {% data variables.product.prodname_codeql_runner %}, to run {% data variables.product.prodname_code_scanning %} in your CI system, by default the runner automatically uploads results to {% data variables.product.prodname_dotcom %} on completion. If you block the automatic upload, when you are ready to upload results you can use the `upload` command (for more information, see "[Running {% data variables.product.prodname_codeql_runner %} in your CI system](/code-security/secure-coding/running-codeql-runner-in-your-ci-system)"). - The {% data variables.product.prodname_codeql_runner %}, to run {% data variables.product.prodname_code_scanning %} in your CI system, by default the runner automatically uploads results to {% data variables.product.prodname_dotcom %} on completion. If you block the automatic upload, when you are ready to upload results you can use the `upload` command (for more information, see "[Running {% data variables.product.prodname_codeql_runner %} in your CI system](/code-security/secure-coding/running-codeql-runner-in-your-ci-system)").
- A tool that generates results as an artifact outside of your repository, you can use the {% data variables.product.prodname_code_scanning %} API to upload the file (for more information, see "[Upload an analysis as SARIF data](/rest/reference/code-scanning#upload-an-analysis-as-sarif-data)"). - A tool that generates results as an artifact outside of your repository, you can use the {% data variables.product.prodname_code_scanning %} API to upload the file (for more information, see "[Upload an analysis as SARIF data](/rest/reference/code-scanning#upload-an-analysis-as-sarif-data)").

View File

@@ -19,6 +19,8 @@ topics:
redirect_from: redirect_from:
- /code-security/secure-coding/about-codeql-code-scanning-in-your-ci-system - /code-security/secure-coding/about-codeql-code-scanning-in-your-ci-system
--- ---
<!--UI-LINK: When GitHub Enterprise Server 3.1+ doesn't have GitHub Actions set up, the Security > Code scanning alerts view links to this article.-->
{% data reusables.code-scanning.beta %} {% data reusables.code-scanning.beta %}
{% data reusables.code-scanning.enterprise-enable-code-scanning %} {% data reusables.code-scanning.enterprise-enable-code-scanning %}
@@ -26,9 +28,35 @@ redirect_from:
{% data reusables.code-scanning.about-code-scanning %} For information, see "[About {% data variables.product.prodname_code_scanning %}](/code-security/secure-coding/about-code-scanning)." {% data reusables.code-scanning.about-code-scanning %} For information, see "[About {% data variables.product.prodname_code_scanning %}](/code-security/secure-coding/about-code-scanning)."
You can run {% data variables.product.prodname_codeql %} {% data variables.product.prodname_code_scanning %} within {% data variables.product.product_name %} using actions. Alternatively, if you use a third-party continuous integration or continuous delivery/deployment (CI/CD) system, you can run {% data variables.product.prodname_codeql %} analysis in your existing system and upload the results to {% data variables.product.product_location %}. You can run {% data variables.product.prodname_codeql %} {% data variables.product.prodname_code_scanning %} within {% data variables.product.product_name %} using {% data variables.product.prodname_actions %}. Alternatively, if you use a third-party continuous integration or continuous delivery/deployment (CI/CD) system, you can run {% data variables.product.prodname_codeql %} analysis in your existing system and upload the results to {% data variables.product.product_location %}.
{% if currentVersion == "free-pro-team@latest" or currentVersion ver_gt "enterprise-server@3.0" or currentVersion == "github-ae@next" %} <!--Content for GitHub.com, GHAE next, and GHES 3.2. Both CodeQL CLI and CodeQL runner are available, but CodeQL CLI preferred -->
{% if currentVersion == "free-pro-team@latest" or currentVersion ver_gt "enterprise-server@3.1" or currentVersion == "github-ae@next" %}
You add the {% data variables.product.prodname_codeql_cli %} to your third-party system, then call the tool to analyze code and upload the SARIF results to {% data variables.product.product_name %}. The resulting {% data variables.product.prodname_code_scanning %} alerts are shown alongside any alerts generated within {% data variables.product.product_name %}.
{% data reusables.code-scanning.upload-sarif-ghas %}
## About the {% data variables.product.prodname_codeql_cli %}
{% data reusables.code-scanning.what-is-codeql-cli %}
Use the {% data variables.product.prodname_codeql_cli %} to analyze:
- Dynamic languages, for example, JavaScript and Python.
- Compiled languages, for example, C/C++, C# and Java.
- Codebases written in a mixture of languages.
For more information, see "[Installing {% data variables.product.prodname_codeql_cli %} in your CI system](/code-security/secure-coding/using-codeql-code-scanning-with-your-existing-ci-system/installing-codeql-cli-in-your-ci-system)."
{% data reusables.code-scanning.licensing-note %}
{% data reusables.code-scanning.use-codeql-runner-not-cli %}
{% endif %}
<!--Content for GHES 3.1 only. Both CodeQL CLI and CodeQL runner are available -->
{% if currentVersion == "enterprise-server@3.1" %}
You add the {% data variables.product.prodname_codeql_cli %} or the {% data variables.product.prodname_codeql_runner %} to your third-party system, then call the tool to analyze code and upload the SARIF results to {% data variables.product.product_name %}. The resulting {% data variables.product.prodname_code_scanning %} alerts are shown alongside any alerts generated within {% data variables.product.product_name %}. You add the {% data variables.product.prodname_codeql_cli %} or the {% data variables.product.prodname_codeql_runner %} to your third-party system, then call the tool to analyze code and upload the SARIF results to {% data variables.product.product_name %}. The resulting {% data variables.product.prodname_code_scanning %} alerts are shown alongside any alerts generated within {% data variables.product.product_name %}.
{% data reusables.code-scanning.upload-sarif-ghas %} {% data reusables.code-scanning.upload-sarif-ghas %}
@@ -44,22 +72,18 @@ Use the {% data variables.product.prodname_codeql_cli %} to analyze:
- Dynamic languages, for example, JavaScript and Python. - Dynamic languages, for example, JavaScript and Python.
- Codebases with a compiled language that can be built with a single command or by running a single script. - Codebases with a compiled language that can be built with a single command or by running a single script.
For more information, see "[Running {% data variables.product.prodname_codeql_cli %} in your CI system](/code-security/secure-coding/running-codeql-cli-in-your-ci-system)." For more information, see "[Installing {% data variables.product.prodname_codeql_cli %} in your CI system](/code-security/secure-coding/using-codeql-code-scanning-with-your-existing-ci-system/installing-codeql-cli-in-your-ci-system)."
{% if currentVersion == "free-pro-team@latest" %} {% data reusables.code-scanning.use-codeql-runner-not-cli %}
If you need to set up the CI system to orchestrate compiler invocations as well as running {% data variables.product.prodname_codeql %} analysis, you must use the {% data variables.product.prodname_codeql_runner %}.
{% else %}
You will need to use the {% data variables.product.prodname_codeql_runner %} if you need to:
- Set up the CI system to orchestrate compiler invocations as well as running {% data variables.product.prodname_codeql %} analysis.
- Analyze more than one language in a repository.
{% endif %}
{% data reusables.code-scanning.beta-codeql-runner %} {% data reusables.code-scanning.beta-codeql-runner %}
For more information, see "[Running {% data variables.product.prodname_codeql_runner %} in your CI system](/code-security/secure-coding/running-codeql-runner-in-your-ci-system)." For more information, see "[Running {% data variables.product.prodname_codeql_runner %} in your CI system](/code-security/secure-coding/running-codeql-runner-in-your-ci-system)."
{% else %} {% endif %}
<!--Content for GHAE and GHES 3.0 only. Only CodeQL runner is available -->
{% if currentVersion == "enterprise-server@3.0" or currentVersion == "github-ae@latest" %}
{% data reusables.code-scanning.upload-sarif-ghas %} {% data reusables.code-scanning.upload-sarif-ghas %}
You add the {% data variables.product.prodname_codeql_runner %} to your third-party system, then call the tool to analyze code and upload the SARIF results to {% data variables.product.product_name %}. The resulting {% data variables.product.prodname_code_scanning %} alerts are shown alongside any alerts generated within {% data variables.product.product_name %}. You add the {% data variables.product.prodname_codeql_runner %} to your third-party system, then call the tool to analyze code and upload the SARIF results to {% data variables.product.product_name %}. The resulting {% data variables.product.prodname_code_scanning %} alerts are shown alongside any alerts generated within {% data variables.product.product_name %}.

View File

@@ -0,0 +1,285 @@
---
title: Configuring CodeQL CLI in your CI system
shortTitle: Configuring CodeQL CLI
intro: 'You can configure your continuous integration system to run the {% data variables.product.prodname_codeql_cli %}, perform {% data variables.product.prodname_codeql %} analysis, and upload the results to {% data variables.product.product_name %} for display as {% data variables.product.prodname_code_scanning %} alerts.'
product: '{% data reusables.gated-features.code-scanning %}'
miniTocMaxHeadingLevel: 3
versions:
free-pro-team: '*'
enterprise-server: '>=3.1'
github-ae: 'next'
type: how_to
topics:
- Advanced Security
- Code scanning
- CodeQL
- Repositories
- Pull requests
- Integration
- CI
- SARIF
---
{% data reusables.code-scanning.enterprise-enable-code-scanning %}
## About generating code scanning results with {% data variables.product.prodname_codeql_cli %}
Once you've made the {% data variables.product.prodname_codeql_cli %} available to servers in your CI system, and ensured that they can authenticate with {% data variables.product.product_name %}, you're ready to generate data.
You use three different commands to generate results and upload them to {% data variables.product.product_name %}:
{% if currentVersion == "free-pro-team@latest" or currentVersion ver_gt "enterprise-server@3.1" or currentVersion == "github-ae@next" %}
<!--Option to analyze multiple languages with one call-->
1. `database create` to create a {% data variables.product.prodname_codeql %} database to represent the hierarchical structure of each supported programming language in the repository.
2. ` database analyze` to run queries to analyze each {% data variables.product.prodname_codeql %} database and summarize the results in a SARIF file.
3. `github upload-results` to upload the resulting SARIF files to {% data variables.product.product_name %} where the results are matched to a branch or pull request and displayed as {% data variables.product.prodname_code_scanning %} alerts.
{% else %}
<!--Only one language can be analyzed-->
1. `database create` to create a {% data variables.product.prodname_codeql %} database to represent the hierarchical structure of a supported programming language in the repository.
2. ` database analyze` to run queries to analyze the {% data variables.product.prodname_codeql %} database and summarize the results in a SARIF file.
3. `github upload-results` to upload the resulting SARIF file to {% data variables.product.product_name %} where the results are matched to a branch or pull request and displayed as {% data variables.product.prodname_code_scanning %} alerts.
{% endif %}
You can display the command-line help for any command using the <nobr>`--help`</nobr> option.
{% data reusables.code-scanning.upload-sarif-ghas %}
## Creating {% data variables.product.prodname_codeql %} databases to analyze
1. Check out the code that you want to analyze:
- For a branch, check out the head of the branch that you want to analyze.
- For a pull request, check out either the head commit of the pull request, or check out a {% data variables.product.product_name %}-generated merge commit of the pull request.
2. Set up the environment for the codebase, making sure that any dependencies are available. For more information, see [Creating databases for non-compiled languages](https://codeql.github.com/docs/codeql-cli/creating-codeql-databases/#creating-databases-for-non-compiled-languages) and [Creating databases for compiled languages](https://codeql.github.com/docs/codeql-cli/creating-codeql-databases/#creating-databases-for-compiled-languages) in the documentation for the {% data variables.product.prodname_codeql_cli %}.
3. Find the build command, if any, for the codebase. Typically this is available in a configuration file in the CI system.
4. Run `codeql database create` from the checkout root of your repository and build the codebase.
{% if currentVersion == "free-pro-team@latest" or currentVersion ver_gt "enterprise-server@3.1" or currentVersion == "github-ae@next" %}
```shell
# Single supported language - create one CodeQL databsae
codeql database create &lt;database&gt; --command&lt;build&gt; --language=&lt;language-identifier&gt;
# Multiple supported languages - create one CodeQL database per langauge
codeql database create &lt;database&gt; --command&lt;build&gt; \
--db-cluster --language=&lt;language-identifier&gt;,&lt;language-identifier&gt;
```
{% else %}
```shell
codeql database create &lt;database&gt; --command&lt;build&gt; --language=&lt;language-identifier&gt;
```
{% endif %}
{% note %}
**Note:** If you use a containerized build, you need to run the {% data variables.product.prodname_codeql_cli %} inside the container where your build task takes place.
{% endnote %}
| Option | Required | Usage |
|--------|:--------:|-----|
| `<database>` | {% octicon "check-circle-fill" aria-label="Required" %} | Specify the name and location of a directory to create for the {% data variables.product.prodname_codeql %} database. The command will fail if you try to overwrite an existing directory. If you also specify `--db-cluster`, this is the parent directory and a subdirectory is created for each language analyzed.|
| <nobr>`--language`</nobr> | {% octicon "check-circle-fill" aria-label="Required" %} | Specify the identifier for the language to create a database for, one of: `{% data reusables.code-scanning.codeql-languages-keywords %}` (use `javascript` to analyze TypeScript code). {% if currentVersion == "free-pro-team@latest" or currentVersion ver_gt "enterprise-server@3.1" or currentVersion == "github-ae@next" %}When used with <nobr>`--db-cluster`</nobr>, the option accepts a comma-separated list, or can be specified more than once.{% endif %}
| <nobr>`--command`</nobr> | | Recommended. Use to specify the build command or script that invokes the build process for the codebase. Commands are run from the current folder or, where it is defined, from <nobr>`--source-root`</nobr>. Not needed for Python and JavaScript/TypeScript analysis. | {% if currentVersion == "free-pro-team@latest" or currentVersion ver_gt "enterprise-server@3.1" or currentVersion == "github-ae@next" %}
| <nobr>`--db-cluster`</nobr> | | Optional. Use in multi-language codebases to generate one database for each language specified by <nobr>`--language`</nobr>.
| <nobr>`--no-run-unnecessary-builds`</nobr> | | Recommended. Use to suppress the build command for languages where the {% data variables.product.prodname_codeql_cli %} does not need to monitor the build (for example, Python and JavaScript/TypeScript). {% endif %}
| <nobr>`--source-root`</nobr> | | Optional. Use if you run the CLI outside the checkout root of the repository. By default, the `database create` command assumes that the current directory is the root directory for the source files, use this option to specify a different location. |
For more information, see [Creating {% data variables.product.prodname_codeql %} databases](https://codeql.github.com/docs/codeql-cli/creating-codeql-databases/) in the documentation for the {% data variables.product.prodname_codeql_cli %}.
### {% if currentVersion == "free-pro-team@latest" or currentVersion ver_gt "enterprise-server@3.1" or currentVersion == "github-ae@next" %}Single language example{% else %}Basic example{% endif %}
This example creates a {% data variables.product.prodname_codeql %} database for the repository checked out at `/checkouts/example-repo`. It uses the JavaScript extractor to create a hierarchical representation of the JavaScript and TypeScript code in the repository. The resulting database is stored in `/codeql-dbs/example-repo`.
```
$ codeql database create /codeql-dbs/example-repo --language=javascript \
--source-root /checkouts/example-repo
> Initializing database at /codeql-dbs/example-repo.
> Running command [/codeql-home/codeql/javascript/tools/autobuild.cmd]
in /checkouts/example-repo.
> [build-stdout] Single-threaded extraction.
> [build-stdout] Extracting
...
> Finalizing database at /codeql-dbs/example-repo.
> Successfully created database at /codeql-dbs/example-repo.
```
{% if currentVersion == "free-pro-team@latest" or currentVersion ver_gt "enterprise-server@3.1" or currentVersion == "github-ae@next" %}
### Multiple language example
This example creates two {% data variables.product.prodname_codeql %} databases for the repository checked out at `/checkouts/example-repo-multi`. It uses:
- `--db-cluster` to request analysis of more than one language.
- `--language` to specify which languages to create databases for.
- `--command` to tell the tool the build command for the codebase, here `make`.
- `--no-run-unnecessary-builds` to tell the tool to skip the build command for languages where it is not needed (like Python).
The resulting databases are stored in `python` and `cpp` subdirectories of `/codeql-dbs/example-repo-multi`.
```
$ codeql database create /codeql-dbs/example-repo-multi \
--db-cluster --language python,cpp \
--command make --no-run-unnecessary-builds \
--source-root /checkouts/example-repo-multi
Initializing databases at /codeql-dbs/example-repo-multi.
Running build command: [make]
[build-stdout] Calling python3 /codeql-bundle/codeql/python/tools/get_venv_lib.py
[build-stdout] Calling python3 -S /codeql-bundle/codeql/python/tools/python_tracer.py -v -z all -c /codeql-dbs/example-repo-multi/python/working/trap_cache -p ERROR: 'pip' not installed.
[build-stdout] /usr/local/lib/python3.6/dist-packages -R /checkouts/example-repo-multi
[build-stdout] [INFO] Python version 3.6.9
[build-stdout] [INFO] Python extractor version 5.16
[build-stdout] [INFO] [2] Extracted file /checkouts/example-repo-multi/hello.py in 5ms
[build-stdout] [INFO] Processed 1 modules in 0.15s
[build-stdout] <output from calling 'make' to build the C/C++ code>
Finalizing databases at /codeql-dbs/example-repo-multi.
Successfully created databases at /codeql-dbs/example-repo-multi.
$
```
{% endif %}
## Analyzing a {% data variables.product.prodname_codeql %} database
1. Create a {% data variables.product.prodname_codeql %} database (see above).
2. Run `codeql database analyze` on the database and specify which queries to use.
```shell
codeql database analyze &lt;database&gt; --format=&lt;format&gt; \
--output=&lt;output&gt; &lt;queries&gt;
```
{% if currentVersion == "free-pro-team@latest" or currentVersion ver_gt "enterprise-server@3.1" or currentVersion == "github-ae@next" %}
{% note %}
**Note:** If you analyze more than one {% data variables.product.prodname_codeql %} database for a single commit, you must specify a SARIF category for each set of results generated by this command. When you upload the results to {% data variables.product.product_name %}, {% data variables.product.prodname_code_scanning %} uses this category to store the results for each language separately. If you forget to do this, each upload overwrites the previous results.
```shell
codeql database analyze &lt;database&gt; --format=&lt;format&gt; \
--sarif-category=&lt;language-specifier&gt; --output=&lt;output&gt; &lt;queries&gt;
```
{% endnote %}
{% endif %}
| Option | Required | Usage |
|--------|:--------:|-----|
| `<database>` | {% octicon "check-circle-fill" aria-label="Required" %} | Specify the path for the directory that contains the {% data variables.product.prodname_codeql %} database to analyze. |
| `<queries>` | {% octicon "check-circle-fill" aria-label="Required" %} | Specify the queries to run. To run the standard queries used for {% data variables.product.prodname_code_scanning %}, use: `<language>-code-scanning.qls` where `<language>` is the short code for the language of the database. To see the other query suites included in the {% data variables.product.prodname_codeql_cli %} bundle, look in `/<extraction-root>/codeql/qlpacks/codeql-<language>/codeql-suites`. For information about creating your own query suite, see [Creating CodeQL query suites](https://codeql.github.com/docs/codeql-cli/creating-codeql-query-suites/) in the documentation for the {% data variables.product.prodname_codeql_cli %}.
| <nobr>`--format`</nobr> | {% octicon "check-circle-fill" aria-label="Required" %} | Specify the format for the results file generated by the command. For upload to {% data variables.product.company_short %} this should be: {% if currentVersion == "free-pro-team@latest" or currentVersion == "github-ae@latest" %}`sarif-latest`{% else %}`sarifv2.1.0`{% endif %}. For more information, see "[SARIF support for {% data variables.product.prodname_code_scanning %}](/code-security/secure-coding/sarif-support-for-code-scanning)."
| <nobr>`--output`</nobr> | {% octicon "check-circle-fill" aria-label="Required" %} | Specify where to save the SARIF results file.{% if currentVersion == "free-pro-team@latest" or currentVersion ver_gt "enterprise-server@3.1" or currentVersion == "github-ae@next" %}
| <nobr>`--sarif-category`<nobr> | {% octicon "question" aria-label="Required with multiple results sets" %} | Optional for single database analysis. Required to define the language when you analyze multiple databases for a single commit in a repository. Specify a category to include in the SARIF results file for this analysis. A category is used to distinguish multiple analyses for the same tool and commit, but performed on different languages or different parts of the code.|{% endif %}
| <nobr>`--threads`</nobr> | | Optional. Use if you want to use more than one thread to run queries. The default value is `1`. You can specify more threads to speed up query execution. To set the number of threads to the number of logical processors, specify `0`.
| <nobr>`--verbose`</nobr> | | Optional. Use to get more detailed information about the analysis process{% if currentVersion == "free-pro-team@latest" or currentVersion ver_gt "enterprise-server@3.1" or currentVersion == "github-ae@next" %} and diagnostic data from the database creation process{% endif %}.
For more information, see [Analyzing databases with the {% data variables.product.prodname_codeql_cli %}](https://codeql.github.com/docs/codeql-cli/analyzing-databases-with-the-codeql-cli/) in the documentation for the {% data variables.product.prodname_codeql_cli %}.
### Basic example
This example analyzes a {% data variables.product.prodname_codeql %} database stored at `/codeql-dbs/example-repo` and saves the results as a SARIF file: `/temp/example-repo-js.sarif`. {% if currentVersion == "free-pro-team@latest" or currentVersion ver_gt "enterprise-server@3.1" or currentVersion == "github-ae@next" %}It uses `--sarif-category` to include extra information in the SARIF file that identifies the results as JavaScript. This is essential when you have more than one {% data variables.product.prodname_codeql %} database to analyze for a single commit in a repository.{% endif %}
```
$ codeql database analyze /codeql-dbs/example-repo \
javascript-code-scanning.qls {% if currentVersion == "free-pro-team@latest" or currentVersion ver_gt "enterprise-server@3.1" or currentVersion == "github-ae@next" %}--sarif-category=javascript{% endif %}
--format={% if currentVersion == "free-pro-team@latest" or currentVersion == "github-ae@latest" %}sarif-latest{% else %}sarifv2.1.0{% endif %} --output=/temp/example-repo-js.sarif
> Running queries.
> Compiling query plan for /codeql-home/codeql/qlpacks/
codeql-javascript/AngularJS/DisablingSce.ql.
...
> Shutting down query evaluator.
> Interpreting results.
```
## Uploading results to {% data variables.product.product_name %}
{% data reusables.code-scanning.upload-sarif-alert-limit %}
Before you can upload results to {% data variables.product.product_name %}, you must determine the best way to pass the {% data variables.product.prodname_github_app %} or personal access token you created earlier to the {% data variables.product.prodname_codeql_cli %} (see [Installing {% data variables.product.prodname_codeql_cli %} in your CI system](/code-security/secure-coding/using-codeql-code-scanning-with-your-existing-ci-system/installing-codeql-cli-in-your-ci-system#generating-a-token-for-authentication-with-github)). We recommend that you review your CI system's guidance on the secure use of a secret store. The {% data variables.product.prodname_codeql_cli %} supports:
- Passing the token to the CLI via standard input using the `--github-auth-stdin` option (recommended).
- Saving the secret in the environment variable `GITHUB_TOKEN` and running the CLI without including the `--github-auth-stdin` option.
When you have decided on the most secure and reliable method for your CI server, run `codeql github upload-results` on each SARIF results file and include `--github-auth-stdin` unless the token is available in the environment variable `GITHUB_TOKEN`.
```shell
echo "$UPLOAD_TOKEN" | codeql github upload-results --repository=&lt;repository-name&gt; \
--ref=&lt;ref&gt; --commit=&lt;commit&gt; --sarif=&lt;file&gt; \
{% if currentVersion ver_gt "enterprise-server@3.0" or currentVersion == "github-ae@next" %}--github-url=&lt;URL&gt; {% endif %}--github-auth-stdin
```
| Option | Required | Usage |
|--------|:--------:|-----|
| <nobr>`--repository`</nobr> | {% octicon "check-circle-fill" aria-label="Required" %} | Specify the *OWNER/NAME* of the repository to upload data to. The owner must be an organization within an enterprise that has a license for {% data variables.product.prodname_GH_advanced_security %} and {% data variables.product.prodname_GH_advanced_security %} must be enabled for the repository{% if currentVersion == "free-pro-team@latest" %}, unless the repository is public{% endif %}. For more information, see "[Managing security and analysis settings for your repository](/github/administering-a-repository/managing-security-and-analysis-settings-for-your-repository)."
| <nobr>`--ref`</nobr> | {% octicon "check-circle-fill" aria-label="Required" %} | Specify the name of the `ref` you checked out and analyzed so that the results can be matched to the correct code. For a branch use: `refs/heads/BRANCH-NAME`, for the head commit of a pull request use `refs/pulls/NUMBER/head`, or for the {% data variables.product.product_name %}-generated merge commit of a pull request use `refs/pulls/NUMBER/merge`.
| <nobr>`--commit`</nobr> | {% octicon "check-circle-fill" aria-label="Required" %} | Specify the full SHA of the commit you analyzed.
| <nobr>`--sarif`</nobr> | {% octicon "check-circle-fill" aria-label="Required" %} | Specify the SARIF file to load.{% if currentVersion ver_gt "enterprise-server@3.0" or currentVersion == "github-ae@next" %}
| <nobr>`--github-url`</nobr> | {% octicon "check-circle-fill" aria-label="Required" %} | Specify the URL for {% data variables.product.product_name %}.{% endif %}
| <nobr>`--github-auth-stdin`</nobr> | | Optional. Use to pass the CLI the {% data variables.product.prodname_github_app %} or personal access token created for authentication with {% data variables.product.company_short %}'s REST API via standard input. This is not needed if the command has access to a `GITHUB_TOKEN` environment variable set with this token.
For more information, see [github upload-results](https://codeql.github.com/docs/codeql-cli/manual/github-upload-results/) in the documentation for the {% data variables.product.prodname_codeql_cli %}.
### Basic example
This example uploads results from the SARIF file `temp/example-repo-js.sarif` to the repository `my-org/example-repo`. It tells the {% data variables.product.prodname_code_scanning %} API that the results are for the commit `deb275d2d5fe9a522a0b7bd8b6b6a1c939552718` on the `main` branch.
```
$ echo $UPLOAD_TOKEN | codeql github upload-results --repository=my-org/example-repo \
--ref=refs/heads/main --commit=deb275d2d5fe9a522a0b7bd8b6b6a1c939552718 \
--sarif=/temp/example-repo-js.sarif {% if currentVersion ver_gt "enterprise-server@3.0" or currentVersion == "github-ae@next" %}--github-url={% data variables.command_line.git_url_example %} \
{% endif %}--github-auth-stdin
```
There is no output from this command unless the upload was unsuccessful. The command prompt returns when the upload is complete and data processing has begun. On smaller codebases, you should be able to explore the {% data variables.product.prodname_code_scanning %} alerts in {% data variables.product.product_name %} shortly afterward. You can see alerts directly in the pull request or on the **Security** tab for branches, depending on the code you checked out. For more information, see "[Triaging {% data variables.product.prodname_code_scanning %} alerts in pull requests](/code-security/secure-coding/triaging-code-scanning-alerts-in-pull-requests)" and "[Managing {% data variables.product.prodname_code_scanning %} alerts for your repository](/code-security/secure-coding/managing-code-scanning-alerts-for-your-repository)."
{% if currentVersion == "free-pro-team@latest" or currentVersion ver_gt "enterprise-server@3.1" or currentVersion == "github-ae@next" %}
## Example CI configuration for {% data variables.product.prodname_codeql %} analysis
This is an example of the series of commands that you might use to analyze a codebase with two supported languages and then upload the results to {% data variables.product.product_name %}.
```shell
# Create CodeQL databases for Java and Python in the 'codeql-dbs' directory
# Call the normal build script for the codebase: 'myBuildScript'
codeql database create codeql-dbs --source-root=src \
--db-cluster --language=java,python --command=./myBuildScript
# Analyze the CodeQL database for Java, 'codeql-dbs/java'
# Tag the data as 'java' results and store in: 'java-results.sarif'
codeql database analyze codeql-dbs/java java-code-scanning.qls \
--format=sarif-latest --sarif-category=java --output=java-results.sarif
# Analyze the CodeQL database for Python, 'codeql-dbs/python'
# Tag the data as 'python' results and store in: 'python-results.sarif'
codeql database analyze codeql-dbs/python python-code-scanning.qls \
--format=sarif-latest --sarif-category=python --output=python-results.sarif
# Upload the SARIF file with the Java results: 'java-results.sarif'
echo $UPLOAD_TOKEN | codeql github upload-results --repository=my-org/example-repo \
--ref=refs/heads/main --commit=deb275d2d5fe9a522a0b7bd8b6b6a1c939552718 \
--sarif=java-results.sarif --github-auth-stdin
# Upload the SARIF file with the Python results: 'python-results.sarif'
echo $UPLOAD_TOKEN | codeql github upload-results --repository=my-org/example-repo \
--ref=refs/heads/main --commit=deb275d2d5fe9a522a0b7bd8b6b6a1c939552718 \
--sarif=python-results.sarif --github-auth-stdin
```
## Troubleshooting the {% data variables.product.prodname_codeql_cli %} in your CI system
### Diagnostic data and summary metrics
When you analyze a {% data variables.product.prodname_codeql %} database using a {% data variables.product.prodname_code_scanning %} query suite, in addition to generating detailed information about alerts, the CLI reports diagnostic data from the database generation step and summary metrics. For repositories with few alerts, you may find this information useful for determining if there are genuinely few problems in the code, or if there were errors generating the {% data variables.product.prodname_codeql %} database. For more detailed output from `codeql database analyze`, use the `--verbose` option.
### {% data variables.product.prodname_code_scanning %} only shows analysis results from one of the analized languages
By default, {% data variables.product.prodname_code_scanning %} expects one SARIF results file per analysis for a repository. Consequently, when you upload a second SARIF results file for a commit, it is treated as a replacement for the original set of data.
If you want to upload more than one set of results to the {% data variables.product.prodname_code_scanning %} API for a commit in a repository, you must identify each set of results as a unique set. For repositories where you create more than one {% data variables.product.prodname_codeql %} database to analyze for each commit, use the `--sarif-category` option to specify a language or other unique category for each SARIF file that you generate for that repository.
### Alternative if your CI system cannot trigger the {% data variables.product.prodname_codeql_cli %}
{% data reusables.code-scanning.use-codeql-runner-not-cli %}
{% endif %}
## Further reading
- [Creating CodeQL databases](https://codeql.github.com/docs/codeql-cli/creating-codeql-databases/)
- [Analyzing databases with the CodeQL CLI](https://codeql.github.com/docs/codeql-cli/analyzing-databases-with-the-codeql-cli/)

View File

@@ -18,7 +18,8 @@ topics:
- CodeQL - CodeQL
children: children:
- /about-codeql-code-scanning-in-your-ci-system - /about-codeql-code-scanning-in-your-ci-system
- /running-codeql-cli-in-your-ci-system - /installing-codeql-cli-in-your-ci-system
- /configuring-codeql-cli-in-your-ci-system
- /running-codeql-runner-in-your-ci-system - /running-codeql-runner-in-your-ci-system
- /configuring-codeql-runner-in-your-ci-system - /configuring-codeql-runner-in-your-ci-system
- /troubleshooting-codeql-runner-in-your-ci-system - /troubleshooting-codeql-runner-in-your-ci-system

View File

@@ -0,0 +1,99 @@
---
title: Installing CodeQL CLI in your CI system
shortTitle: Installing CodeQL CLI
intro: 'You can install the {% data variables.product.prodname_codeql_cli %} and use it to perform {% data variables.product.prodname_codeql %} {% data variables.product.prodname_code_scanning %} in a third-party continuous integration system.'
product: '{% data reusables.gated-features.code-scanning %}'
miniTocMaxHeadingLevel: 3
versions:
free-pro-team: '*'
enterprise-server: '>=3.1'
github-ae: 'next'
type: how_to
topics:
- Advanced Security
- Code scanning
- CodeQL
- Repositories
- Pull requests
- Integration
- CI
- SARIF
redirect_from:
- /code-security/secure-coding/running-codeql-cli-in-your-ci-system
- /code-security/secure-coding/using-codeql-code-scanning-with-your-existing-ci-system/running-codeql-cli-in-your-ci-system
---
{% data reusables.code-scanning.enterprise-enable-code-scanning %}
## About using the {% data variables.product.prodname_codeql_cli %} for {% data variables.product.prodname_code_scanning %}
You can use the {% data variables.product.prodname_codeql_cli %} to run {% data variables.product.prodname_code_scanning %} on code that you're processing in a third-party continuous integration (CI) system. {% data reusables.code-scanning.about-code-scanning %} For information, see "[About {% data variables.product.prodname_code_scanning %}](/code-security/secure-coding/about-code-scanning)."
{% data reusables.code-scanning.what-is-codeql-cli %}
Alternatively, you can use {% data variables.product.prodname_actions %} to run {% data variables.product.prodname_code_scanning %} within {% data variables.product.product_name %}. For information about {% data variables.product.prodname_code_scanning %} using actions, see "[Setting up {% data variables.product.prodname_code_scanning %} for a repository](/code-security/secure-coding/setting-up-code-scanning-for-a-repository)." For an overview of the options for CI systems, see "[About CodeQL {% data variables.product.prodname_code_scanning %} in your CI system](/code-security/secure-coding/about-codeql-code-scanning-in-your-ci-system)".
{% data reusables.code-scanning.licensing-note %}
## Downloading the {% data variables.product.prodname_codeql_cli %}
You should download the {% data variables.product.prodname_codeql %} bundle from https://github.com/github/codeql-action/releases. The bundle contains:
- {% data variables.product.prodname_codeql_cli %} product
- A compatible version of the queries and libraries from https://github.com/github/codeql
- Precompiled versions of all the queries included in the bundle
You should always use the {% data variables.product.prodname_codeql %} bundle as this ensures compatibility and also gives much better performance than a separate download of the {% data variables.product.prodname_codeql_cli %} and checkout of the {% data variables.product.prodname_codeql %} queries. If you will only be running the CLI on one specific platform, download the appropriate `codeql-bundle-PLATFORM.tar.gz` file. Alternatively, you can download `codeql-bundle.tar.gz`, which contains the CLI for all supported platforms.
## Setting up the {% data variables.product.prodname_codeql_cli %} in your CI system
You need to make the full contents of the {% data variables.product.prodname_codeql_cli %} bundle available to every CI server that you want to run CodeQL {% data variables.product.prodname_code_scanning %} analysis on. For example, you might configure each server to copy the bundle from a central, internal location and extract it. Alternatively, you could use the REST API to get the bundle directly from {% data variables.product.prodname_dotcom %}, ensuring that you benefit from the latest improvements to queries. Updates to the {% data variables.product.prodname_codeql_cli %} are released every 2-3 weeks. For example:
```shell
$ wget https://{% if currentVersion == "free-pro-team@latest" %}github.com{% else %}<em>HOSTNAME</em>{% endif %}/github/codeql-action/releases/latest/download/codeql-bundle-linux64.tar.gz
$ tar -xvzf ../codeql-bundle-linux64.tar.gz
```
After you extract the {% data variables.product.prodname_codeql_cli %} bundle, you can run the `codeql` executable on the server:
- By executing `/<extraction-root>/codeql/codeql`, where `<extraction-root>` is the folder where you extracted the {% data variables.product.prodname_codeql_cli %} bundle.
- By adding `/<extraction-root>/codeql` to your `PATH`, so that you can run the executable as just `codeql`.
## Testing the {% data variables.product.prodname_codeql_cli %} set up
After you extract the {% data variables.product.prodname_codeql_cli %} bundle, you can run the following command to verify that the CLI is correctly set up to create and analyze databases.
- `codeql resolve qlpacks` if `/<extraction-root>/codeql` is on the `PATH`.
- `/<extraction-root>/codeql/codeql resolve qlpacks` otherwise.
**Extract from successful output:**
```
codeql-cpp (/<extraction-root>/codeql/qlpacks/codeql-cpp)
codeql-cpp-examples (/<extraction-root>/codeql/qlpacks/codeql-cpp-examples)
codeql-cpp-upgrades (/<extraction-root>/codeql/qlpacks/codeql-cpp-upgrades)
codeql-csharp (/<extraction-root>/codeql/qlpacks/codeql-csharp)
codeql-csharp-examples (/<extraction-root>/codeql/qlpacks/codeql-csharp-examples)
codeql-csharp-upgrades (/<extraction-root>/codeql/qlpacks/codeql-csharp-upgrades)
codeql-go (/<extraction-root>/codeql/qlpacks/codeql-go)
codeql-go-examples (/<extraction-root>/codeql/qlpacks/codeql-go-examples)
codeql-go-upgrades (/<extraction-root>/codeql/qlpacks/codeql-go-upgrades)
codeql-java (/<extraction-root>/codeql/qlpacks/codeql-java)
codeql-java-examples (/<extraction-root>/codeql/qlpacks/codeql-java-examples)
codeql-java-upgrades (/<extraction-root>/codeql/qlpacks/codeql-java-upgrades)
codeql-javascript (/<extraction-root>/codeql/qlpacks/codeql-javascript)
codeql-javascript-examples (/<extraction-root>/codeql/qlpacks/codeql-javascript-examples)
codeql-javascript-upgrades (/<extraction-root>/codeql/qlpacks/codeql-javascript-upgrades)
codeql-python (/<extraction-root>/codeql/qlpacks/codeql-python)
codeql-python-examples (/<extraction-root>/codeql/qlpacks/codeql-python-examples)
codeql-python-upgrades (/<extraction-root>/codeql/qlpacks/codeql-python-upgrades)
...
```
You should check that the output contains the expected languages and also that the directory location for the qlpack files is correct. The location should be within the extracted {% data variables.product.prodname_codeql_cli %} bundle, shown above as `<extraction root>`, unless you are using a checkout of `github/codeql`. If the {% data variables.product.prodname_codeql_cli %} is unable to locate the qlpacks for the expected languages, check that you downloaded the {% data variables.product.prodname_codeql %} bundle and not a standalone copy of the {% data variables.product.prodname_codeql_cli %}.
## Generating a token for authentication with {% data variables.product.product_name %}
Each CI server needs a {% data variables.product.prodname_github_app %} or personal access token for the {% data variables.product.prodname_codeql_cli %} to use to upload results to {% data variables.product.product_name %}. You must use an access token or a {% data variables.product.prodname_github_app %} with the `security_events` write permission. If CI servers already use a token with this scope to checkout repositories from {% data variables.product.product_name %}, you could potentially allow the {% data variables.product.prodname_codeql_cli %} to use the same token. Otherwise, you should create a new token with the `security_events` write permission and add this to the CI system's secret store. For information, see "[Building {% data variables.product.prodname_github_apps %}](/developers/apps/building-github-apps)" and "[Creating a personal access token](/github/authenticating-to-github/creating-a-personal-access-token)."
## Next steps
You're now ready to configure the CI system to run {% data variables.product.prodname_codeql %} analysis, generate results, and upload them to {% data variables.product.product_name %} where the results will be matched to a branch or pull request and displayed as {% data variables.product.prodname_code_scanning %} alerts. For detailed information, see "[Configuring {% data variables.product.prodname_codeql_cli %} in your CI system](/code-security/secure-coding/using-codeql-code-scanning-with-your-existing-ci-system/configuring-codeql-cli-in-your-ci-system)."

View File

@@ -1,225 +0,0 @@
---
title: Running CodeQL CLI in your CI system
shortTitle: Running CodeQL CLI
intro: 'You can use the {% data variables.product.prodname_codeql_cli %} to perform {% data variables.product.prodname_codeql %} {% data variables.product.prodname_code_scanning %} in a third-party continuous integration system.'
product: '{% data reusables.gated-features.code-scanning %}'
miniTocMaxHeadingLevel: 3
versions:
free-pro-team: '*'
enterprise-server: '>=3.1'
github-ae: 'next'
type: how_to
topics:
- Advanced Security
- Code scanning
- CodeQL
- Repositories
- Pull requests
- Integration
- CI
- SARIF
redirect_from:
- /code-security/secure-coding/running-codeql-cli-in-your-ci-system
---
{% data reusables.code-scanning.enterprise-enable-code-scanning %}
## About the {% data variables.product.prodname_codeql_cli %}
You can use the {% data variables.product.prodname_codeql_cli %} to run {% data variables.product.prodname_code_scanning %} on code that you're processing in a third-party continuous integration (CI) system. {% data reusables.code-scanning.about-code-scanning %} For information, see "[About {% data variables.product.prodname_code_scanning %}](/code-security/secure-coding/about-code-scanning)."
{% data reusables.code-scanning.what-is-codeql-cli %}
Alternatively, you can use {% data variables.product.prodname_codeql_runner %} in your CI system, or {% data variables.product.prodname_actions %} to run {% data variables.product.prodname_code_scanning %} within {% data variables.product.product_name %}. For an overview of the options for CI systems, see "[About CodeQL {% data variables.product.prodname_code_scanning %} in your CI system](/code-security/secure-coding/about-codeql-code-scanning-in-your-ci-system)". For information about {% data variables.product.prodname_code_scanning %} using actions, see "[Setting up {% data variables.product.prodname_code_scanning %} for a repository](/code-security/secure-coding/setting-up-code-scanning-for-a-repository)."
{% note %}
**Note:** {% if currentVersion == "free-pro-team@latest" %}
The {% data variables.product.prodname_codeql_cli %} is free to use on public repositories that are maintained on {% data variables.product.prodname_dotcom_the_website %}, and available to use on private repositories that are owned by customers with an {% data variables.product.prodname_advanced_security %} license. For information, see "[{% data variables.product.product_name %} {% data variables.product.prodname_codeql %} Terms and Conditions](https://securitylab.github.com/tools/codeql/license)" and "[{% data variables.product.prodname_codeql %} CLI](https://codeql.github.com/docs/codeql-cli/)."
{%- else %}The {% data variables.product.prodname_codeql_cli %} is available to customers with an {% data variables.product.prodname_advanced_security %} license.
{% endif %}
{% endnote %}
## Downloading the {% data variables.product.prodname_codeql_cli %}
You should download the {% data variables.product.prodname_codeql %} bundle from https://github.com/github/codeql-action/releases. The bundle contains:
- {% data variables.product.prodname_codeql_cli %} product
- A compatible version of the queries and libraries from https://github.com/github/codeql
- Precompiled versions of all the queries included in the bundle
You should always use the {% data variables.product.prodname_codeql %} bundle as this ensures compatibility and also gives much better performance than a separate download of the {% data variables.product.prodname_codeql_cli %} and checkout of the {% data variables.product.prodname_codeql %} queries. If you will only be running the CLI on one specific platform, download the appropriate `codeql-bundle-PLATFORM.tar.gz` file. Alternatively, you can download `codeql-bundle.tar.gz`, which contains the CLI for all supported platforms.
## Setting up the {% data variables.product.prodname_codeql_cli %} in your CI system
You need to make the full contents of the {% data variables.product.prodname_codeql_cli %} bundle available to every CI server that you want to run CodeQL {% data variables.product.prodname_code_scanning %} analysis on. For example, you might configure each server to copy the bundle from a central, internal location and extract it. Alternatively, you could use the REST API to get the bundle directly from {% data variables.product.prodname_dotcom %}, ensuring that you benefit from the latest improvements to queries. Updates to the {% data variables.product.prodname_codeql_cli %} are released every 2-3 weeks. For example:
```shell
$ wget https://{% if currentVersion == "free-pro-team@latest" %}github.com{% else %}<em>HOSTNAME</em>{% endif %}/github/codeql-action/releases/latest/download/codeql-bundle-linux64.tar.gz
$ tar -xvzf ../codeql-bundle-linux64.tar.gz
```
After you extract the {% data variables.product.prodname_codeql_cli %} bundle, you can run the `codeql` executable on the server:
- By executing `/extraction-root/codeql/codeql`, where `<extraction-root>` is the folder where you extracted the {% data variables.product.prodname_codeql_cli %} bundle.
- By adding `/extraction-root/codeql` to your `PATH`, so that you can run the executable as just `codeql`.
## Testing the {% data variables.product.prodname_codeql_cli %} set up
After you extract the {% data variables.product.prodname_codeql_cli %} bundle, you can run the following command to verify that the CLI is correctly set up to create and analyze databases.
- `codeql resolve languages` if `/extraction-root/codeql` is on the `PATH`.
- `/extraction-root/codeql/codeql resolve languages` otherwise.
**Example of successful output:**
```
cpp (/extraction-root/codeql/cpp)
csharp (/extraction-root/codeql/csharp)
csv (/extraction-root/codeql/csv)
go (/extraction-root/codeql/go)
html (/extraction-root/codeql/html)
java (/extraction-root/codeql/java)
javascript (/extraction-root/codeql/javascript)
properties (/extraction-root/codeql/properties)
python (/extraction-root/codeql/python)
xml (/extraction-root/codeql/xml)
```
If the {% data variables.product.prodname_codeql_cli %} is unable to resolve the expected languages, check that you downloaded the {% data variables.product.prodname_codeql %} bundle and not a standalone copy of the {% data variables.product.prodname_codeql_cli %}.
## Generating a token for authentication with {% data variables.product.product_name %}
Each CI server needs a {% data variables.product.prodname_github_app %} or personal access token for the {% data variables.product.prodname_codeql_cli %} to use to upload results to {% data variables.product.product_name %}. You must use an access token or a {% data variables.product.prodname_github_app %} with the `security_events` write permission. If CI servers already use a token with this scope to checkout repositories from {% data variables.product.product_name %}, you could potentially allow the {% data variables.product.prodname_codeql_cli %} to use the same token. Otherwise, you should create a new token with the `security_events` write permission and add this to the CI system's secret store. For information, see "[Building {% data variables.product.prodname_github_apps %}](/developers/apps/building-github-apps)" and "[Creating a personal access token](/github/authenticating-to-github/creating-a-personal-access-token)."
## Using the {% data variables.product.prodname_codeql_cli %} to generate data and upload it to {% data variables.product.product_name %}
You call the {% data variables.product.prodname_codeql_cli %} to analyze the codebase in three steps:
1. Create a {% data variables.product.prodname_codeql %} database to represent a single programming language in the repository using: `codeql database create`
2. Run queries to analyze the {% data variables.product.prodname_codeql %} database and summarize the results in a SARIF file using: `codeql database analyze`
3. Upload the SARIF file to {% data variables.product.product_name %} where the results are matched to a branch or pull request and displayed as {% data variables.product.prodname_code_scanning %} alerts using: `codeql github upload-results`
Each command has a few mandatory options with additional options that you can use to modify the behavior of the command. You can display the command-line help for any command using the <nobr>`--help`</nobr> option.
{% data reusables.code-scanning.upload-sarif-ghas %}
### Creating a {% data variables.product.prodname_codeql %} database to analyze
1. Check out the code that you want to analyze:
- For a branch checkout the head of the branch that you want to analyze.
- For a pull request checkout either the head commit of the pull request, or check out a {% data variables.product.product_name %}-generated merge commit of the pull request.
2. Set up the environment for the codebase, making sure that any dependencies are available. For more information, see [Creating databases for non-compiled languages](https://codeql.github.com/docs/codeql-cli/creating-codeql-databases/#creating-databases-for-non-compiled-languages) and [Creating databases for compiled languages](https://codeql.github.com/docs/codeql-cli/creating-codeql-databases/#creating-databases-for-compiled-languages) in the documentation for the {% data variables.product.prodname_codeql_cli %}.
3. Run `codeql database create` from the checkout root of your repository.
```shell
codeql database create &lt;database&gt; --language=&lt;language-identifier&gt;
```
{% note %}
**Note:** If you use a containerized build, you need to run the {% data variables.product.prodname_codeql_cli %} inside the container where your build task takes place.
{% endnote %}
| Option | Required | Usage |
|--------|:--------:|-----|
| `<database>` | {% octicon "check-circle-fill" aria-label="Required" %} | Specify the name and location of a directory to create for the {% data variables.product.prodname_codeql %} database. The command will fail if you try to overwrite an existing directory. |
| <nobr>`--language`</nobr> | {% octicon "check-circle-fill" aria-label="Required" %} | Specify the identifier for the language to create a database for, one of: `{% data reusables.code-scanning.codeql-languages-keywords %}` (use `javascript` to analyze TypeScript code).
| <nobr>`--source-root`</nobr> | | Optional. Use if you run the CLI outside the checkout root of the repository. By default, the `database create` command assumes that the current directory is the root directory for the source files, use this option to specify a different location. |
| <nobr>`--command`</nobr> | | Optional for compiled languages. Use if you want to override the CLI's automatic build system detection and compilation. Specify the build command or script that invokes the compiler. Commands are run from the current folder or, where it is defined, from <nobr>`--source-root`</nobr>. Do not use this option for Python and JavaScript/TypeScript analysis. |
For more information, see [Creating {% data variables.product.prodname_codeql %} databases](https://codeql.github.com/docs/codeql-cli/creating-codeql-databases/) in the documentation for the {% data variables.product.prodname_codeql_cli %}.
#### Basic example
```
$ codeql database create /codeql-dbs/example-repo --language=javascript \
--source-root /checkouts/example-repo
> Initializing database at /codeql-dbs/example-repo.
> Running command [/codeql-home/codeql/javascript/tools/autobuild.cmd]
in /checkouts/example-repo.
> [build-stdout] Single-threaded extraction.
> [build-stdout] Extracting
...
> Finalizing database at /codeql-dbs/example-repo.
> Successfully created database at /codeql-dbs/example-repo.
```
For more information and examples, see [Creating {% data variables.product.prodname_codeql %} databases ](https://codeql.github.com/docs/codeql-cli/creating-codeql-databases) in the documentation for the {% data variables.product.prodname_codeql_cli %}.
### Analyzing a {% data variables.product.prodname_codeql %} database
1. Create a {% data variables.product.prodname_codeql %} database (see above).
2. Run `codeql database analyze` on the database and specify which queries to use.
```shell
codeql database analyze &lt;database&gt; --format=&lt;format&gt; \
--output=&lt;output&gt; &lt;queries&gt;
```
| Option | Required | Usage |
|--------|:--------:|-----|
| `<database>` | {% octicon "check-circle-fill" aria-label="Required" %} | Specify the path for the directory that contains the {% data variables.product.prodname_codeql %} database to analyze. |
| `<queries>` | {% octicon "check-circle-fill" aria-label="Required" %} | Specify the queries to run. To run the standard queries used for {% data variables.product.prodname_code_scanning %}, use: `<language>-code-scanning.qls` where `<language>` is the short code for the language of the database. To see the other query suites included in the {% data variables.product.prodname_codeql_cli %} bundle look in `/extraction-root/codeql/qlpacks/codeql-<language>/codeql-suites`. For information about creating your own query suite, see [Creating CodeQL query suites](https://codeql.github.com/docs/codeql-cli/creating-codeql-query-suites/) in the documentation for the {% data variables.product.prodname_codeql_cli %}.
| <nobr>`--format`</nobr> | {% octicon "check-circle-fill" aria-label="Required" %} | Specify the format for the results file generated by the command. For upload to {% data variables.product.company_short %} this should be: {% if currentVersion == "free-pro-team@latest" %}`sarif-latest`{% else %}`sarifv2.1.0`{% endif %}. For more information, see "[SARIF support for {% data variables.product.prodname_code_scanning %}](/code-security/secure-coding/sarif-support-for-code-scanning)."
| <nobr>`--output`</nobr> | {% octicon "check-circle-fill" aria-label="Required" %} | Specify where to save the SARIF results file.{% if currentVersion == "free-pro-team@latest" %}
| <nobr>`--sarif-category`<nobr> | | Optional. Specify a category to include in the SARIF results file for this analysis. A category can be used to distinguish multiple analyses for the same tool and commit, but performed on different languages or different parts of the code. This value will appear in the `<run>.automationId` property in SARIF v1, the `<run>.automationLogicalId` property in SARIF v2, and the `<run>.automationDetails.id` property in SARIF v2.1.0. |{% endif %}
| <nobr>`--threads`</nobr> | | Optional. Use if you want to use more than one thread to run queries. The default value is `1`. You can specify more threads to speed up query execution. To set the number of threads to the number of logical processors, specify `0`.
For more information, see [Analyzing databases with the {% data variables.product.prodname_codeql_cli %}](https://codeql.github.com/docs/codeql-cli/analyzing-databases-with-the-codeql-cli/) in the documentation for the {% data variables.product.prodname_codeql_cli %}.
#### Basic example
```
$ codeql database analyze /codeql-dbs/example-repo \
javascript-code-scanning.qls --format={% if currentVersion == "free-pro-team@latest" %}sarif-latest{% else %}sarifv2.1.0{% endif %} \
--output=/temp/example-repo-js.sarif
> Running queries.
> Compiling query plan for /codeql-home/codeql/qlpacks/
codeql-javascript/AngularJS/DisablingSce.ql.
...
> Shutting down query evaluator.
> Interpreting results.
```
### Uploading results to {% data variables.product.product_name %}
{% data reusables.code-scanning.upload-sarif-alert-limit %}
Before you can upload results to {% data variables.product.product_name %}, you must determine the best way to pass the {% data variables.product.prodname_github_app %} or personal access token you created earlier to the {% data variables.product.prodname_codeql_cli %} (see [Generating a token for authentication with {% data variables.product.product_name %}](#generating-a-token-for-authentication-with-github) above). We recommend that you review your CI system's guidance on the secure use of the secret store. The {% data variables.product.prodname_codeql_cli %} supports:
- Passing the token to the CLI via standard input using the `--github-auth-stdin` option (recommended).
- Saving the secret in the environment variable `GITHUB_TOKEN` and running the CLI without including the `--github-auth-stdin` option.
When you have decided on the most secure and reliable method for your CI server, run `codeql github upload-results` on the SARIF results file and include `--github-auth-stdin` unless the token is available in the environment variable `GITHUB_TOKEN`.
```shell
echo "$UPLOAD_TOKEN" | codeql github upload-results --repository=&lt;repository-name&gt; \
--ref=&lt;ref&gt; --commit=&lt;commit&gt; --sarif=&lt;file&gt; \
{% if currentVersion ver_gt "enterprise-server@3.0" or currentVersion == "github-ae@next" %}--github-url=&lt;URL&gt; {% endif %}--github-auth-stdin
```
| Option | Required | Usage |
|--------|:--------:|-----|
| <nobr>`--repository`</nobr> | {% octicon "check-circle-fill" aria-label="Required" %} | Specify the *OWNER/NAME* of the repository to upload data to. The owner must be an organization within an enterprise that has a license for {% data variables.product.prodname_GH_advanced_security %} and {% data variables.product.prodname_GH_advanced_security %} must be enabled for the repository{% if currentVersion == "free-pro-team@latest" %}, unless the repository is public{% endif %}. For more information, see "[Managing security and analysis settings for your repository](/github/administering-a-repository/managing-security-and-analysis-settings-for-your-repository)."
| <nobr>`--ref`</nobr> | {% octicon "check-circle-fill" aria-label="Required" %} | Specify the name of the `ref` you checked out and analyzed so that the results can be matched to the correct code. For a branch use: `refs/heads/BRANCH-NAME`, for the head commit of a pull request use `refs/pulls/NUMBER/head`, or for the {% data variables.product.product_name %}-generated merge commit of a pull request use `refs/pulls/NUMBER/merge`.
| <nobr>`--commit`</nobr> | {% octicon "check-circle-fill" aria-label="Required" %} | Specify the full SHA of the commit you analyzed.
| <nobr>`--sarif`</nobr> | {% octicon "check-circle-fill" aria-label="Required" %} | Specify the SARIF file to load.{% if currentVersion ver_gt "enterprise-server@3.0" or currentVersion == "github-ae@next" %}
| <nobr>`--github-url`</nobr> | {% octicon "check-circle-fill" aria-label="Required" %} | Specify the URL for {% data variables.product.product_name %}.{% endif %}
| <nobr>`--github-auth-stdin`</nobr> | | Optional. Use to pass the CLI the {% data variables.product.prodname_github_app %} or personal access token created for authentication with {% data variables.product.company_short %}'s REST API via standard input. This is not needed if the command has access to a `GITHUB_TOKEN` environment variable set with this token.
For more information, see [github upload-results](https://codeql.github.com/docs/codeql-cli/manual/github-upload-results/) in the documentation for the {% data variables.product.prodname_codeql_cli %}.
#### Basic example
```
$ echo $UPLOAD_TOKEN | codeql github upload-results --repository=my-org/example-repo \
--ref=refs/heads/main --commit=deb275d2d5fe9a522a0b7bd8b6b6a1c939552718 \
--sarif=/temp/example-repo-js.sarif {% if currentVersion ver_gt "enterprise-server@3.0" or currentVersion == "github-ae@next" %}--github-url={% data variables.command_line.git_url_example %} \
{% endif %}--github-auth-stdin
```
There is no output from this command unless the upload was unsuccessful. The command prompt returns when the upload is complete and data processing has begun. On smaller codebases, you should be able to explore the {% data variables.product.prodname_code_scanning %} alerts in {% data variables.product.product_name %} shortly afterward. Alerts are shown directly in the pull request or on the **Security** tab for branches, depending on the code that was checked out. For more information, see "[Triaging {% data variables.product.prodname_code_scanning %} alerts in pull requests](/code-security/secure-coding/triaging-code-scanning-alerts-in-pull-requests)" and "[Managing {% data variables.product.prodname_code_scanning %} alerts for your repository](/code-security/secure-coding/managing-code-scanning-alerts-for-your-repository)."
## Further reading
- [Creating CodeQL databases](https://codeql.github.com/docs/codeql-cli/creating-codeql-databases/)
- [Analyzing databases with the CodeQL CL](https://codeql.github.com/docs/codeql-cli/analyzing-databases-with-the-codeql-cli/)

View File

@@ -24,7 +24,7 @@ topics:
- SARIF - SARIF
--- ---
<!--For this article in earlier GHES versions, see /content/github/finding-security-vulnerabilities-and-errors-in-your-code--> <!--For this article in earlier GHES versions, see /content/github/finding-security-vulnerabilities-and-errors-in-your-code-->
<!--UI-LINK: When GitHub Enterprise Server doesn't have GitHub Actions set up, the Security > Code scanning alerts view links to this article.--> <!--UI-LINK: When GitHub Enterprise Server <=3.0 doesn't have GitHub Actions set up, the Security > Code scanning alerts view links to this article.-->
{% data reusables.code-scanning.beta-codeql-runner %} {% data reusables.code-scanning.beta-codeql-runner %}
{% data reusables.code-scanning.beta %} {% data reusables.code-scanning.beta %}
@@ -44,7 +44,7 @@ The {% data variables.product.prodname_codeql_runner %} is a command-line tool t
{% note %} {% note %}
**Note:** **Note:**
{% if currentVersion == "free-pro-team@latest" %} {% if currentVersion == "free-pro-team@latest" %}
* The {% data variables.product.prodname_codeql_runner %} uses the {% data variables.product.prodname_codeql %} CLI to analyze code and therefore has the same license conditions. It's free to use on public repositories that are maintained on {% data variables.product.prodname_dotcom_the_website %}, and available to use on private repositories that are owned by customers with an {% data variables.product.prodname_advanced_security %} license. For information, see "[{% data variables.product.product_name %} {% data variables.product.prodname_codeql %} Terms and Conditions](https://securitylab.github.com/tools/codeql/license)" and "[{% data variables.product.prodname_codeql %} CLI](https://codeql.github.com/docs/codeql-cli/)." * The {% data variables.product.prodname_codeql_runner %} uses the {% data variables.product.prodname_codeql %} CLI to analyze code and therefore has the same license conditions. It's free to use on public repositories that are maintained on {% data variables.product.prodname_dotcom_the_website %}, and available to use on private repositories that are owned by customers with an {% data variables.product.prodname_advanced_security %} license. For information, see "[{% data variables.product.product_name %} {% data variables.product.prodname_codeql %} Terms and Conditions](https://securitylab.github.com/tools/codeql/license)" and "[{% data variables.product.prodname_codeql %} CLI](https://codeql.github.com/docs/codeql-cli/)."
{% else %} {% else %}

View File

@@ -70,11 +70,10 @@ Discover interesting projects using {% data variables.explore.explore_github %},
## Celebrate ## Celebrate
You're now connected to the {% data variables.product.product_name %} community. What do you want to do next? You're now connected to the {% data variables.product.product_name %} community. What do you want to do next?
![Star a project](/assets/images/help/stars/star-a-project.png) ![Star a project](/assets/images/help/stars/star-a-project.png)
- "[Set up Git](/articles/set-up-git)"
- "[Create a repository](/articles/create-a-repo)" - To synchronize your {% data variables.product.product_name %} projects with your computer, you can set up Git. For more information see "[Set up Git](/articles/set-up-git)."
- "[Fork a repository](/articles/fork-a-repo)" - You can also create a repository, where you can put all your projects and maintain your workflows. For more information see, "[Create a repository](/articles/create-a-repo)."
- **Be social** - You can fork a repository to make changes you want to see without affecting the original repository. For more information, see "[Fork a repository](/articles/fork-a-repo)."
- {% data reusables.support.connect-in-the-forum-bootcamp %} - {% data reusables.support.connect-in-the-forum-bootcamp %}

View File

@@ -79,11 +79,12 @@ Let's commit a change to the *README* file.
## Celebrate ## Celebrate
Congratulations! You have now created a repository, including a *README* file, and created your first commit on {% data variables.product.product_location %}. What do you want to do next? Congratulations! You have now created a repository, including a *README* file, and created your first commit on {% data variables.product.product_location %}.
- "[Set up Git](/articles/set-up-git)" You can now clone a {% data variables.product.product_name %} repository to create a local copy on your computer. From your local repository you can commit, and create a pull request to update the changes in the upstream repository. For more information, see "[Cloning a repository](/github/creating-cloning-and-archiving-repositories/cloning-a-repository)" and "[Set up Git](/articles/set-up-git)."
- **Create a repository**
- "[Clone a repository](/github/creating-cloning-and-archiving-repositories/cloning-a-repository)" You can find interesting projects and repositories on {% data variables.product.product_name %} and make changes to them by creating a fork of the repository. For more information see, "[Fork a repository](/articles/fork-a-repo)."
- "[Fork a repository](/articles/fork-a-repo)"
- "[Be social](/articles/be-social)" Each repository in {% data variables.product.product_name %} is owned by a person or an organization. You can interact with the people, repositories, and organizations by connecting and following them on {% data variables.product.product_name %}. For more information see "[Be social](/articles/be-social)."
- {% data reusables.support.connect-in-the-forum-bootcamp %}
{% data reusables.support.connect-in-the-forum-bootcamp %}

View File

@@ -148,10 +148,10 @@ Fork a repository to start contributing to a project. {% data reusables.reposito
## Celebrate ## Celebrate
You have now forked a repository, practiced cloning your fork, and configured an upstream repository. What do you want to do next? You have now forked a repository, practiced cloning your fork, and configured an upstream repository. For more information about cloning the fork and syncing the changes in a forked repository from your computer see "[Set up Git](/articles/set-up-git)."
- "[Set up Git](/articles/set-up-git)" You can also create a new repository where you can put all your projects and share the code on {% data variables.product.prodname_dotcom %}. For more information see, "[Create a repository](/articles/create-a-repo)."
- "[Create a repository](/articles/create-a-repo)"
- **Fork a repository** Each repository in {% data variables.product.product_name %} is owned by a person or an organization. You can interact with the people, repositories, and organizations by connecting and following them on {% data variables.product.product_name %}. For more information see "[Be social](/articles/be-social)."
- "[Be social](/articles/be-social)"
- {% data reusables.support.connect-in-the-forum-bootcamp %} {% data reusables.support.connect-in-the-forum-bootcamp %}

View File

@@ -56,10 +56,10 @@ If you [clone with SSH](/github/getting-started-with-github/about-remote-reposit
## Celebrate ## Celebrate
Congratulations, you now have Git and {% data variables.product.product_name %} all set up! What do you want to do next? Congratulations, you now have Git and {% data variables.product.product_name %} all set up! You may now choose to create a repository where you can put your projects. This is a great way to back up your code and makes it easy to share the code around the world. For more information see "[Create a repository](/articles/create-a-repo)".
- **Set up Git** You can create a copy of a repository by forking it and propose the changes that you want to see without affecting the upstream repository. For more information see "[Fork a repository](/articles/fork-a-repo)."
- "[Create a repository](/articles/create-a-repo)"
- "[Fork a repository](/articles/fork-a-repo)" Each repository in {% data variables.product.product_name %} is owned by a person or an organization. You can interact with the people, repositories, and organizations by connecting and following them on {% data variables.product.product_name %}. For more information see "[Be social](/articles/be-social)."
- "[Be social](/articles/be-social)"
- {% data reusables.support.connect-in-the-forum-bootcamp %} {% data reusables.support.connect-in-the-forum-bootcamp %}

View File

@@ -33,8 +33,8 @@ topics:
```shell ```shell
$ gpg --default-new-key-algo rsa4096 --gen-key $ gpg --default-new-key-algo rsa4096 --gen-key
``` ```
4. At the prompt, specify the kind of key you want, or press `Enter` to accept the default `RSA and RSA`. 4. At the prompt, specify the kind of key you want, or press `Enter` to accept the default.
5. Enter the desired key size. Your key must be at least `4096` bits. 5. At the prompt, specify the key size you want, or press `Enter` to accept the default. Your key must be at least `4096` bits.
6. Enter the length of time the key should be valid. Press `Enter` to specify the default selection, indicating that the key doesn't expire. 6. Enter the length of time the key should be valid. Press `Enter` to specify the default selection, indicating that the key doesn't expire.
7. Verify that your selections are correct. 7. Verify that your selections are correct.
8. Enter your user ID information. 8. Enter your user ID information.

View File

@@ -1,5 +1,6 @@
--- ---
title: Comparing commits title: Comparing commits
intro: You can compare the state of your repository across branches, tags, commits, forks, and dates.
redirect_from: redirect_from:
- /articles/comparing-commits-across-time - /articles/comparing-commits-across-time
- /github/committing-changes-to-your-project/comparing-commits-across-time - /github/committing-changes-to-your-project/comparing-commits-across-time
@@ -9,7 +10,7 @@ versions:
enterprise-server: '*' enterprise-server: '*'
github-ae: '*' github-ae: '*'
--- ---
You can compare the state of your repository across branches, tags, commits, and dates. To compare different versions of your repository, append `/compare` to your repository's path. To compare different versions of your repository, append `/compare` to your repository's path.
We'll demonstrate the power of Compare by looking at the compare page for [a fork of the Linguist repo](https://github.com/octocat/linguist), which is at [https://github.com/octocat/linguist/compare/master...octocat:master](https://github.com/octocat/linguist/compare/master...octocat:master). We'll demonstrate the power of Compare by looking at the compare page for [a fork of the Linguist repo](https://github.com/octocat/linguist), which is at [https://github.com/octocat/linguist/compare/master...octocat:master](https://github.com/octocat/linguist/compare/master...octocat:master).

View File

@@ -1,5 +1,6 @@
--- ---
title: Differences between commit views title: Differences between commit views
intro: You might observe differences in commit history depending on the chosen viewing method.
redirect_from: redirect_from:
- /articles/differences-between-commit-views - /articles/differences-between-commit-views
- /github/committing-changes-to-your-project/differences-between-commit-views - /github/committing-changes-to-your-project/differences-between-commit-views

View File

@@ -735,6 +735,36 @@ type App implements Node {
description: String description: String
id: ID! id: ID!
"""
The IP addresses of the app.
"""
ipAllowListEntries(
"""
Returns the elements in the list that come after the specified cursor.
"""
after: String
"""
Returns the elements in the list that come before the specified cursor.
"""
before: String
"""
Returns the first _n_ elements from the list.
"""
first: Int
"""
Returns the last _n_ elements from the list.
"""
last: Int
"""
Ordering options for IP allow list entries returned.
"""
orderBy: IpAllowListEntryOrder = {field: ALLOW_LIST_VALUE, direction: ASC}
): IpAllowListEntryConnection!
""" """
The hex color code, without the leading '#', for the logo background. The hex color code, without the leading '#', for the logo background.
""" """
@@ -5599,7 +5629,7 @@ input CreateIpAllowListEntryInput {
""" """
The ID of the owner for which to create the new IP allow list entry. The ID of the owner for which to create the new IP allow list entry.
""" """
ownerId: ID! @possibleTypes(concreteTypes: ["Enterprise", "Organization"], abstractType: "IpAllowListOwner") ownerId: ID! @possibleTypes(concreteTypes: ["App", "Enterprise", "Organization"], abstractType: "IpAllowListOwner")
} }
""" """
@@ -12851,7 +12881,7 @@ enum IpAllowListForInstalledAppsEnabledSettingValue {
""" """
Types that can own an IP allow list. Types that can own an IP allow list.
""" """
union IpAllowListOwner = Enterprise | Organization union IpAllowListOwner = App | Enterprise | Organization
""" """
An Issue is a place to discuss ideas, enhancements, tasks, and bugs for a project. An Issue is a place to discuss ideas, enhancements, tasks, and bugs for a project.
@@ -38626,7 +38656,7 @@ input UpdateIpAllowListEnabledSettingInput {
""" """
The ID of the owner on which to set the IP allow list enabled setting. The ID of the owner on which to set the IP allow list enabled setting.
""" """
ownerId: ID! @possibleTypes(concreteTypes: ["Enterprise", "Organization"], abstractType: "IpAllowListOwner") ownerId: ID! @possibleTypes(concreteTypes: ["App", "Enterprise", "Organization"], abstractType: "IpAllowListOwner")
""" """
The value for the IP allow list enabled setting. The value for the IP allow list enabled setting.
@@ -38706,7 +38736,7 @@ input UpdateIpAllowListForInstalledAppsEnabledSettingInput {
""" """
The ID of the owner. The ID of the owner.
""" """
ownerId: ID! @possibleTypes(concreteTypes: ["Enterprise", "Organization"], abstractType: "IpAllowListOwner") ownerId: ID! @possibleTypes(concreteTypes: ["App", "Enterprise", "Organization"], abstractType: "IpAllowListOwner")
""" """
The value for the IP allow list configuration for installed GitHub Apps setting. The value for the IP allow list configuration for installed GitHub Apps setting.

View File

@@ -825,6 +825,36 @@ type App implements Node {
description: String description: String
id: ID! id: ID!
"""
The IP addresses of the app.
"""
ipAllowListEntries(
"""
Returns the elements in the list that come after the specified cursor.
"""
after: String
"""
Returns the elements in the list that come before the specified cursor.
"""
before: String
"""
Returns the first _n_ elements from the list.
"""
first: Int
"""
Returns the last _n_ elements from the list.
"""
last: Int
"""
Ordering options for IP allow list entries returned.
"""
orderBy: IpAllowListEntryOrder = {field: ALLOW_LIST_VALUE, direction: ASC}
): IpAllowListEntryConnection!
""" """
The hex color code, without the leading '#', for the logo background. The hex color code, without the leading '#', for the logo background.
""" """
@@ -5754,7 +5784,7 @@ input CreateIpAllowListEntryInput {
""" """
The ID of the owner for which to create the new IP allow list entry. The ID of the owner for which to create the new IP allow list entry.
""" """
ownerId: ID! @possibleTypes(concreteTypes: ["Enterprise", "Organization"], abstractType: "IpAllowListOwner") ownerId: ID! @possibleTypes(concreteTypes: ["App", "Enterprise", "Organization"], abstractType: "IpAllowListOwner")
} }
""" """
@@ -7315,13 +7345,20 @@ type DependencyGraphDependency @preview(toggledBy: "hawkgirl-preview") {
""" """
hasDependencies: Boolean! hasDependencies: Boolean!
"""
The original name of the package, as it appears in the manifest.
"""
packageLabel: String!
""" """
The dependency package manager The dependency package manager
""" """
packageManager: String packageManager: String
""" """
The required package name The name of the package in the canonical form used by the package manager.
This may differ from the original textual form (see packageLabel), for example
in a package manager that uses case-insensitive comparisons.
""" """
packageName: String! packageName: String!
@@ -13597,7 +13634,7 @@ enum IpAllowListForInstalledAppsEnabledSettingValue {
""" """
Types that can own an IP allow list. Types that can own an IP allow list.
""" """
union IpAllowListOwner = Enterprise | Organization union IpAllowListOwner = App | Enterprise | Organization
""" """
An Issue is a place to discuss ideas, enhancements, tasks, and bugs for a project. An Issue is a place to discuss ideas, enhancements, tasks, and bugs for a project.
@@ -41950,7 +41987,7 @@ input UpdateIpAllowListEnabledSettingInput {
""" """
The ID of the owner on which to set the IP allow list enabled setting. The ID of the owner on which to set the IP allow list enabled setting.
""" """
ownerId: ID! @possibleTypes(concreteTypes: ["Enterprise", "Organization"], abstractType: "IpAllowListOwner") ownerId: ID! @possibleTypes(concreteTypes: ["App", "Enterprise", "Organization"], abstractType: "IpAllowListOwner")
""" """
The value for the IP allow list enabled setting. The value for the IP allow list enabled setting.
@@ -42030,7 +42067,7 @@ input UpdateIpAllowListForInstalledAppsEnabledSettingInput {
""" """
The ID of the owner. The ID of the owner.
""" """
ownerId: ID! @possibleTypes(concreteTypes: ["Enterprise", "Organization"], abstractType: "IpAllowListOwner") ownerId: ID! @possibleTypes(concreteTypes: ["App", "Enterprise", "Organization"], abstractType: "IpAllowListOwner")
""" """
The value for the IP allow list configuration for installed GitHub Apps setting. The value for the IP allow list configuration for installed GitHub Apps setting.

View File

@@ -102,7 +102,8 @@ code_security_ci:
description: 'Set up CodeQL within your existing CI and upload results to GitHub code scanning.' description: 'Set up CodeQL within your existing CI and upload results to GitHub code scanning.'
guides: guides:
- /code-security/secure-coding/using-codeql-code-scanning-with-your-existing-ci-system/about-codeql-code-scanning-in-your-ci-system - /code-security/secure-coding/using-codeql-code-scanning-with-your-existing-ci-system/about-codeql-code-scanning-in-your-ci-system
- /code-security/secure-coding/using-codeql-code-scanning-with-your-existing-ci-system/running-codeql-cli-in-your-ci-system - /code-security/secure-coding/using-codeql-code-scanning-with-your-existing-ci-system/installing-codeql-cli-in-your-ci-system
- /code-security/secure-coding/using-codeql-code-scanning-with-your-existing-ci-system/configuring-codeql-cli-in-your-ci-system
- /code-security/secure-coding/using-codeql-code-scanning-with-your-existing-ci-system/running-codeql-runner-in-your-ci-system - /code-security/secure-coding/using-codeql-code-scanning-with-your-existing-ci-system/running-codeql-runner-in-your-ci-system
- /code-security/secure-coding/using-codeql-code-scanning-with-your-existing-ci-system/configuring-codeql-runner-in-your-ci-system - /code-security/secure-coding/using-codeql-code-scanning-with-your-existing-ci-system/configuring-codeql-runner-in-your-ci-system
- /code-security/secure-coding/using-codeql-code-scanning-with-your-existing-ci-system/troubleshooting-codeql-runner-in-your-ci-system - /code-security/secure-coding/using-codeql-code-scanning-with-your-existing-ci-system/troubleshooting-codeql-runner-in-your-ci-system

View File

@@ -0,0 +1,7 @@
{% note %}
**Note:** {% if currentVersion == "free-pro-team@latest" %}
The {% data variables.product.prodname_codeql_cli %} is free to use on public repositories that are maintained on {% data variables.product.prodname_dotcom_the_website %}, and available to use on private repositories that are owned by customers with an {% data variables.product.prodname_advanced_security %} license. For information, see "[{% data variables.product.product_name %} {% data variables.product.prodname_codeql %} Terms and Conditions](https://securitylab.github.com/tools/codeql/license)" and "[{% data variables.product.prodname_codeql %} CLI](https://codeql.github.com/docs/codeql-cli/)."
{%- else %}The {% data variables.product.prodname_codeql_cli %} is available to customers with an {% data variables.product.prodname_advanced_security %} license.
{% endif %}
{% endnote %}

View File

@@ -0,0 +1,9 @@
{% if currentVersion == "free-pro-team@latest" or currentVersion ver_gt "enterprise-server@3.1" or currentVersion == "github-ae@next" %}
If the {% data variables.product.prodname_codeql_cli %} is unsuitable for use in your CI system, the {% data variables.product.prodname_codeql_runner %} is available as an alternative. Typically, this is needed if the CI system would need to orchestrate compiler invocations as well as running {% data variables.product.prodname_codeql %} analysis. For more information, see "[Running {% data variables.product.prodname_codeql_runner %} in your CI system](/code-security/secure-coding/using-codeql-code-scanning-with-your-existing-ci-system/running-codeql-runner-in-your-ci-system)."
{% endif %}
{% if currentVersion == "enterprise-server@3.1" %}
You will need to use the {% data variables.product.prodname_codeql_runner %} if you need to:
- Set up the CI system to orchestrate compiler invocations as well as running {% data variables.product.prodname_codeql %} analysis.
- Analyze more than one language in a repository.
{% endif %}

View File

@@ -1 +1 @@
Connect with people around the world in the [{% data variables.product.prodname_gcf %}](https://github.community) {% data variables.product.product_name %} has a great support community where you can ask for help and talk to people from around the world. Join the conversation on [Github Support Community](https://github.community/).

View File

@@ -1,7 +1,7 @@
<head> <head>
{% comment %} For human readers {% endcomment %} {% comment %} For human readers {% endcomment %}
<meta charset="utf-8" /> <meta charset="utf-8" />
<title>{% if error == '404' %}{% data ui.errors.oops %}{% elsif currentVersion == 'homepage' %}GitHub Documentation{% elsif page.fullTitle %}{{ page.fullTitle }}{% else %}GitHub Documentation{% endif %}</title> <title>{% if error == '404' %}{% data ui.errors.oops %}{% elsif page.documentType == 'homepage' and currentVersion == 'free-pro-team@latest' %}GitHub Documentation{% elsif page.fullTitle %}{{ page.fullTitle }}{% else %}GitHub Documentation{% endif %}</title>
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="alternate icon" type="image/png" href="/assets/images/site/favicon.png"> <link rel="alternate icon" type="image/png" href="/assets/images/site/favicon.png">
<link rel="icon" type="image/svg+xml" href="/assets/images/site/favicon.svg"> <link rel="icon" type="image/svg+xml" href="/assets/images/site/favicon.svg">

View File

@@ -6,23 +6,17 @@
<details class="dropdown-withArrow position-relative details details-reset mr-md-3 close-when-clicked-outside"> <details class="dropdown-withArrow position-relative details details-reset mr-md-3 close-when-clicked-outside">
<summary class="py-2 color-text-primary" role="button" aria-label="Toggle versions list"> <summary class="py-2 color-text-primary" role="button" aria-label="Toggle versions list">
<div class="d-flex flex-items-center flex-justify-between"> <div class="d-flex flex-items-center flex-justify-between">
{% if currentVersion == 'homepage' %}
{% data ui.homepage.version_picker %}
{% else %}
{{ allVersions[currentVersion].versionTitle }} {{ allVersions[currentVersion].versionTitle }}
{% endif %}
<svg class="arrow ml-md-1" width="14px" height="8px" viewBox="0 0 14 8" xml:space="preserve" fill="none" stroke="currentColor"><path d="M1,1l6.2,6L13,1"></path></svg> <svg class="arrow ml-md-1" width="14px" height="8px" viewBox="0 0 14 8" xml:space="preserve" fill="none" stroke="currentColor"><path d="M1,1l6.2,6L13,1"></path></svg>
</div> </div>
</summary> </summary>
<div id="versions-selector" class="position-md-absolute nav-desktop-langDropdown p-md-4 right-md-n4 top-md-6" style="z-index: 6;"> <div id="versions-selector" class="position-md-absolute nav-desktop-langDropdown p-md-4 right-md-n4 top-md-6" style="z-index: 6;">
{% for permalink in page.permalinks %} {% for permalink in page.permalinks %}
{% unless permalink.pageVersion == 'homepage' %}
<a <a
href="{{ permalink.href }}" href="{{ permalink.href }}"
class="d-block py-2 no-underline {% if currentPath == permalink.href %}active{% else %}Link--primary{% endif %}" class="d-block py-2 no-underline {% if currentPath == permalink.href %}active{% else %}Link--primary{% endif %}"
style="white-space: nowrap" style="white-space: nowrap"
>{{ allVersions[permalink.pageVersion].versionTitle }}</a> >{{ allVersions[permalink.pageVersion].versionTitle }}</a>
{% endunless %}
{% endfor %} {% endfor %}
{% include all-enterprise-releases-link %} {% include all-enterprise-releases-link %}
</div> </div>

View File

@@ -20,7 +20,7 @@
<h2 class="text-mono f5 text-normal color-text-secondary text-md-center mb-4">{% data ui.homepage.explore_by_product %}</h2> <h2 class="text-mono f5 text-normal color-text-secondary text-md-center mb-4">{% data ui.homepage.explore_by_product %}</h2>
<div class="d-flex flex-wrap gutter gutter-xl-spacious"> <div class="d-flex flex-wrap gutter gutter-xl-spacious">
{% for product in activeProducts %} {% for product in activeProducts %}
{% if product.versions contains currentVersion or currentVersion == 'homepage' %} {% if product.versions contains currentVersion %}
<div class="d-flex flex-column col-12 col-sm-6 col-lg-3 pb-4"> <div class="d-flex flex-column col-12 col-sm-6 col-lg-3 pb-4">
<a class="btn-mktg flex-auto d-flex flex-items-center btn-outline-mktg btn-large-mktg ws-normal " href="{% unless product.external %}/{{ currentLanguage }}{% endunless %}{% if product.versions contains currentVersion %}/{{currentVersion}}/{{product.id}}{% else %}{{product.href}}{% endif %}" {% if product.external %}target="_blank"{% endif %}>{{ product.name }} <a class="btn-mktg flex-auto d-flex flex-items-center btn-outline-mktg btn-large-mktg ws-normal " href="{% unless product.external %}/{{ currentLanguage }}{% endunless %}{% if product.versions contains currentVersion %}/{{currentVersion}}/{{product.id}}{% else %}{{product.href}}{% endif %}" {% if product.external %}target="_blank"{% endif %}>{{ product.name }}
{% if product.external %} {% if product.external %}

View File

@@ -11,9 +11,9 @@ export default function () {
const codeTerms = document.querySelectorAll('#article-contents table code') const codeTerms = document.querySelectorAll('#article-contents table code')
if (!codeTerms) return if (!codeTerms) return
codeTerms.forEach(node => { codeTerms.forEach((node) => {
// Return early if a child node is an anchor element // Return early if a child node is an anchor element
const hasChildAnchor = Array.from(node.childNodes).some(child => child.nodeName === 'A') const hasChildAnchor = Array.from(node.childNodes).some((child) => child.nodeName === 'A')
if (hasChildAnchor) return if (hasChildAnchor) return
// Do the wrapping on the inner text only // Do the wrapping on the inner text only

View File

@@ -1,8 +1,12 @@
const parse = require('./read-frontmatter') const parse = require('./read-frontmatter')
const layoutNames = Object.keys(require('./layouts')).concat([false]) const semver = require('semver')
const layouts = require('./layouts')
const semverValidRange = semver.validRange
const layoutNames = Object.keys(layouts).concat([false])
const semverRange = { const semverRange = {
type: 'string', type: 'string',
conform: require('semver').validRange, conform: semverValidRange,
message: 'Must be a valid SemVer range' message: 'Must be a valid SemVer range'
} }
const versionIds = Object.keys(require('./all-versions')) const versionIds = Object.keys(require('./all-versions'))

View File

@@ -1,4 +1,25 @@
[ [
{
"schemaChanges": [
{
"title": "The GraphQL schema includes these changes:",
"changes": [
"Field `ipAllowListEntries` was added to object type `App`",
"Member `App` was added to Union type `IpAllowListOwner`"
]
}
],
"previewChanges": [
{
"title": "The [Access to a repositories dependency graph preview](/graphql/overview/schema-previews#access-to-a-repositories-dependency-graph-preview) includes these changes:",
"changes": [
"Field `packageLabel` was added to object type `DependencyGraphDependency`"
]
}
],
"upcomingChanges": [],
"date": "2021-06-22"
},
{ {
"schemaChanges": [ "schemaChanges": [
{ {

File diff suppressed because one or more lines are too long

View File

@@ -7004,6 +7004,66 @@
"kind": "scalars", "kind": "scalars",
"href": "/graphql/reference/scalars#string" "href": "/graphql/reference/scalars#string"
}, },
{
"name": "ipAllowListEntries",
"description": "<p>The IP addresses of the app.</p>",
"type": "IpAllowListEntryConnection!",
"id": "ipallowlistentryconnection",
"kind": "objects",
"href": "/graphql/reference/objects#ipallowlistentryconnection",
"arguments": [
{
"name": "after",
"description": "<p>Returns the elements in the list that come after the specified cursor.</p>",
"type": {
"name": "String",
"id": "string",
"kind": "scalars",
"href": "/graphql/reference/scalars#string"
}
},
{
"name": "before",
"description": "<p>Returns the elements in the list that come before the specified cursor.</p>",
"type": {
"name": "String",
"id": "string",
"kind": "scalars",
"href": "/graphql/reference/scalars#string"
}
},
{
"name": "first",
"description": "<p>Returns the first <em>n</em> elements from the list.</p>",
"type": {
"name": "Int",
"id": "int",
"kind": "scalars",
"href": "/graphql/reference/scalars#int"
}
},
{
"name": "last",
"description": "<p>Returns the last <em>n</em> elements from the list.</p>",
"type": {
"name": "Int",
"id": "int",
"kind": "scalars",
"href": "/graphql/reference/scalars#int"
}
},
{
"name": "orderBy",
"description": "<p>Ordering options for IP allow list entries returned.</p>",
"type": {
"name": "IpAllowListEntryOrder",
"id": "ipallowlistentryorder",
"kind": "input-objects",
"href": "/graphql/reference/input-objects#ipallowlistentryorder"
}
}
]
},
{ {
"name": "logoBackgroundColor", "name": "logoBackgroundColor",
"description": "<p>The hex color code, without the leading '#', for the logo background.</p>", "description": "<p>The hex color code, without the leading '#', for the logo background.</p>",
@@ -13540,6 +13600,14 @@
"kind": "scalars", "kind": "scalars",
"href": "/graphql/reference/scalars#boolean" "href": "/graphql/reference/scalars#boolean"
}, },
{
"name": "packageLabel",
"description": "<p>The original name of the package, as it appears in the manifest.</p>",
"type": "String!",
"id": "string",
"kind": "scalars",
"href": "/graphql/reference/scalars#string"
},
{ {
"name": "packageManager", "name": "packageManager",
"description": "<p>The dependency package manager.</p>", "description": "<p>The dependency package manager.</p>",
@@ -13550,7 +13618,7 @@
}, },
{ {
"name": "packageName", "name": "packageName",
"description": "<p>The required package name.</p>", "description": "<p>The name of the package in the canonical form used by the package manager.\nThis may differ from the original textual form (see packageLabel), for example\nin a package manager that uses case-insensitive comparisons.</p>",
"type": "String!", "type": "String!",
"id": "string", "id": "string",
"kind": "scalars", "kind": "scalars",
@@ -66969,6 +67037,11 @@
"href": "/graphql/reference/unions#ipallowlistowner", "href": "/graphql/reference/unions#ipallowlistowner",
"description": "<p>Types that can own an IP allow list.</p>", "description": "<p>Types that can own an IP allow list.</p>",
"possibleTypes": [ "possibleTypes": [
{
"name": "App",
"id": "app",
"href": "/graphql/reference/objects#app"
},
{ {
"name": "Enterprise", "name": "Enterprise",
"id": "enterprise", "id": "enterprise",

View File

@@ -5987,6 +5987,66 @@
"kind": "scalars", "kind": "scalars",
"href": "/graphql/reference/scalars#string" "href": "/graphql/reference/scalars#string"
}, },
{
"name": "ipAllowListEntries",
"description": "<p>The IP addresses of the app.</p>",
"type": "IpAllowListEntryConnection!",
"id": "ipallowlistentryconnection",
"kind": "objects",
"href": "/graphql/reference/objects#ipallowlistentryconnection",
"arguments": [
{
"name": "after",
"description": "<p>Returns the elements in the list that come after the specified cursor.</p>",
"type": {
"name": "String",
"id": "string",
"kind": "scalars",
"href": "/graphql/reference/scalars#string"
}
},
{
"name": "before",
"description": "<p>Returns the elements in the list that come before the specified cursor.</p>",
"type": {
"name": "String",
"id": "string",
"kind": "scalars",
"href": "/graphql/reference/scalars#string"
}
},
{
"name": "first",
"description": "<p>Returns the first <em>n</em> elements from the list.</p>",
"type": {
"name": "Int",
"id": "int",
"kind": "scalars",
"href": "/graphql/reference/scalars#int"
}
},
{
"name": "last",
"description": "<p>Returns the last <em>n</em> elements from the list.</p>",
"type": {
"name": "Int",
"id": "int",
"kind": "scalars",
"href": "/graphql/reference/scalars#int"
}
},
{
"name": "orderBy",
"description": "<p>Ordering options for IP allow list entries returned.</p>",
"type": {
"name": "IpAllowListEntryOrder",
"id": "ipallowlistentryorder",
"kind": "input-objects",
"href": "/graphql/reference/input-objects#ipallowlistentryorder"
}
}
]
},
{ {
"name": "logoBackgroundColor", "name": "logoBackgroundColor",
"description": "<p>The hex color code, without the leading '#', for the logo background.</p>", "description": "<p>The hex color code, without the leading '#', for the logo background.</p>",
@@ -62000,6 +62060,11 @@
"href": "/graphql/reference/unions#ipallowlistowner", "href": "/graphql/reference/unions#ipallowlistowner",
"description": "<p>Types that can own an IP allow list.</p>", "description": "<p>Types that can own an IP allow list.</p>",
"possibleTypes": [ "possibleTypes": [
{
"name": "App",
"id": "app",
"href": "/graphql/reference/objects#app"
},
{ {
"name": "Enterprise", "name": "Enterprise",
"id": "enterprise", "id": "enterprise",

View File

@@ -36,7 +36,7 @@ function getVersionStringFromPath (href) {
// Return immediately if this is a link to the homepage // Return immediately if this is a link to the homepage
if (href === '/') { if (href === '/') {
return 'homepage' return nonEnterpriseDefaultVersion
} }
// Get the first segment // Get the first segment

View File

@@ -2,7 +2,6 @@ const assert = require('assert')
const path = require('path') const path = require('path')
const patterns = require('./patterns') const patterns = require('./patterns')
const allVersions = require('./all-versions') const allVersions = require('./all-versions')
const nonEnterpriseDefaultVersion = require('./non-enterprise-default-version')
const removeFPTFromPath = require('./remove-fpt-from-path') const removeFPTFromPath = require('./remove-fpt-from-path')
class Permalink { class Permalink {
@@ -27,24 +26,10 @@ class Permalink {
assert(languageCode, 'languageCode is required') assert(languageCode, 'languageCode is required')
const permalinks = applicableVersions const permalinks = applicableVersions
// skip the Dotcom homepage here because a special homepage permalink is added below
.filter(pageVersion => !(pageVersion === nonEnterpriseDefaultVersion && relativePath === 'index.md'))
.map(pageVersion => { .map(pageVersion => {
return new Permalink(languageCode, pageVersion, relativePath, title) return new Permalink(languageCode, pageVersion, relativePath, title)
}) })
// special permalink for homepage
if (relativePath === 'index.md') {
const homepagePermalink = {
...permalinks[0],
href: '/' + languageCode,
pageVersion: 'homepage',
pageVersionTitle: permalinks[0].title,
homepage: true
}
permalinks.push(homepagePermalink)
}
return permalinks return permalinks
} }

View File

@@ -1,6 +1,7 @@
const path = require('path') const path = require('path')
const patterns = require('../patterns') const patterns = require('../patterns')
const supportedVersions = new Set(Object.keys(require('../all-versions'))) const allVersions = require('../all-versions')
const supportedVersions = new Set(Object.keys(allVersions))
const getOldPathsFromPermalink = require('./get-old-paths-from-permalink') const getOldPathsFromPermalink = require('./get-old-paths-from-permalink')
const { getVersionStringFromPath } = require('../path-utils') const { getVersionStringFromPath } = require('../path-utils')
const { getNewVersionedPath } = require('../old-versions-utils') const { getNewVersionedPath } = require('../old-versions-utils')

View File

@@ -6,7 +6,7 @@ const operations = {}
fs.readdirSync(schemasPath) fs.readdirSync(schemasPath)
.forEach(filename => { .forEach(filename => {
const key = filename.replace('.json', '') const key = filename.replace('.json', '')
const value = require(path.join(schemasPath, filename)) const value = JSON.parse(fs.readFileSync(path.join(schemasPath, filename)))
operations[key] = value operations[key] = value
}) })
const allVersions = require('../all-versions') const allVersions = require('../all-versions')

View File

@@ -19,7 +19,7 @@ versions.forEach(version => {
// payload file: /path/to/check_run.completed.payload.json // payload file: /path/to/check_run.completed.payload.json
// payload path: check_run.completed // payload path: check_run.completed
const payloadPath = path.basename(payloadFile).replace('.payload.json', '') const payloadPath = path.basename(payloadFile).replace('.payload.json', '')
set(payloadsPerVersion, payloadPath, formatAsJsonCodeBlock(require(payloadFile))) set(payloadsPerVersion, payloadPath, formatAsJsonCodeBlock(JSON.parse(fs.readFileSync(payloadFile))))
}) })
payloads[version] = payloadsPerVersion payloads[version] = payloadsPerVersion

View File

@@ -34,14 +34,14 @@ module.exports = async function contextualize (req, res, next) {
// e.g. searches for "req.context.page" will include results from this file // e.g. searches for "req.context.page" will include results from this file
req.context.currentLanguage = req.language req.context.currentLanguage = req.language
req.context.userLanguage = req.userLanguage req.context.userLanguage = req.userLanguage
req.context.currentVersion = getVersionStringFromPath(req.path) req.context.currentVersion = getVersionStringFromPath(req.pagePath)
req.context.currentProduct = getProductStringFromPath(req.path) req.context.currentProduct = getProductStringFromPath(req.pagePath)
req.context.currentCategory = getCategoryStringFromPath(req.path) req.context.currentCategory = getCategoryStringFromPath(req.pagePath)
req.context.productMap = productMap req.context.productMap = productMap
req.context.activeProducts = activeProducts req.context.activeProducts = activeProducts
req.context.allVersions = allVersions req.context.allVersions = allVersions
req.context.currentPathWithoutLanguage = getPathWithoutLanguage(req.path) req.context.currentPathWithoutLanguage = getPathWithoutLanguage(req.pagePath)
req.context.currentPath = req.path req.context.currentPath = req.pagePath
req.context.query = req.query req.context.query = req.query
req.context.languages = languages req.context.languages = languages
req.context.productNames = productNames req.context.productNames = productNames
@@ -52,13 +52,6 @@ module.exports = async function contextualize (req, res, next) {
req.context.siteTree = siteTree req.context.siteTree = siteTree
req.context.pages = pageMap req.context.pages = pageMap
// TODO we should create new data directories for these example files instead of using variable files
if (productMap[req.context.currentProduct]) {
req.context.productCodeExamples = req.context.site.data.variables[`${productMap[req.context.currentProduct].id}_code_examples`]
req.context.productCommunityExamples = req.context.site.data.variables[`${productMap[req.context.currentProduct].id}_community_examples`]
req.context.productUserExamples = req.context.site.data.variables[`${productMap[req.context.currentProduct].id}_user_examples`]
}
// JS + CSS asset paths // JS + CSS asset paths
req.context.builtAssets = builtAssets req.context.builtAssets = builtAssets

View File

@@ -23,7 +23,7 @@ module.exports = async function genericToc (req, res, next) {
const currentTocType = tocTypes[req.context.page.documentType] const currentTocType = tocTypes[req.context.page.documentType]
// Find the part of the site tree that corresponds to the current path. // Find the part of the site tree that corresponds to the current path.
const treePage = findPageInSiteTree(req.context.currentProductTree, req.context.currentEnglishTree, req.path) const treePage = findPageInSiteTree(req.context.currentProductTree, req.context.currentEnglishTree, req.pagePath)
// Conditionally run getTocItems() recursively. // Conditionally run getTocItems() recursively.
let isRecursive let isRecursive
@@ -50,7 +50,7 @@ module.exports = async function genericToc (req, res, next) {
async function getTocItems (pagesArray, context, isRecursive, renderIntros) { async function getTocItems (pagesArray, context, isRecursive, renderIntros) {
return (await Promise.all(pagesArray.map(async (child) => { return (await Promise.all(pagesArray.map(async (child) => {
// Do not include hidden child items on a TOC page // Do not include hidden child items on a TOC page
if (child.page.hidden) return if (child.page.hidden && !context.currentPath.includes('/early-access/')) return
return { return {
title: child.renderedFullTitle, title: child.renderedFullTitle,

View File

@@ -1,3 +1,5 @@
const fs = require('fs')
const path = require('path')
const previews = require('../../lib/graphql/static/previews') const previews = require('../../lib/graphql/static/previews')
const upcomingChanges = require('../../lib/graphql/static/upcoming-changes') const upcomingChanges = require('../../lib/graphql/static/upcoming-changes')
const changelog = require('../../lib/graphql/static/changelog') const changelog = require('../../lib/graphql/static/changelog')
@@ -13,7 +15,7 @@ module.exports = function graphqlContext (req, res, next) {
const currentVersionObj = allVersions[req.context.currentVersion] const currentVersionObj = allVersions[req.context.currentVersion]
// ignore requests to non-GraphQL reference paths // ignore requests to non-GraphQL reference paths
// and to versions that don't exist // and to versions that don't exist
if (!req.path.includes('/graphql/') || !currentVersionObj) { if (!req.pagePath.includes('/graphql/') || !currentVersionObj) {
return next() return next()
} }
// Get the relevant name of the GraphQL schema files for the current version // Get the relevant name of the GraphQL schema files for the current version
@@ -23,7 +25,7 @@ module.exports = function graphqlContext (req, res, next) {
const graphqlVersion = currentVersionObj.miscVersionName const graphqlVersion = currentVersionObj.miscVersionName
req.context.graphql = { req.context.graphql = {
schemaForCurrentVersion: require(`../../lib/graphql/static/schema-${graphqlVersion}`), schemaForCurrentVersion: JSON.parse(fs.readFileSync(path.join(process.cwd(), `lib/graphql/static/schema-${graphqlVersion}.json`))),
previewsForCurrentVersion: previews[graphqlVersion], previewsForCurrentVersion: previews[graphqlVersion],
upcomingChangesForCurrentVersion: upcomingChanges[graphqlVersion], upcomingChangesForCurrentVersion: upcomingChanges[graphqlVersion],
prerenderedObjectsForCurrentVersion: prerenderedObjects[graphqlVersion], prerenderedObjectsForCurrentVersion: prerenderedObjects[graphqlVersion],

View File

@@ -12,7 +12,7 @@ const supported = all.filter(release => {
module.exports = async function releaseNotesContext (req, res, next) { module.exports = async function releaseNotesContext (req, res, next) {
// The `/release-notes` sub-path // The `/release-notes` sub-path
if (!(req.path.endsWith('/release-notes') || req.path.endsWith('/admin'))) return next() if (!(req.pagePath.endsWith('/release-notes') || req.pagePath.endsWith('/admin'))) return next()
const [requestedPlan, requestedRelease] = req.context.currentVersion.split('@') const [requestedPlan, requestedRelease] = req.context.currentVersion.split('@')
const releaseNotesPerPlan = req.context.site.data['release-notes'][requestedPlan] const releaseNotesPerPlan = req.context.site.data['release-notes'][requestedPlan]
@@ -28,7 +28,7 @@ module.exports = async function releaseNotesContext (req, res, next) {
if (hasNumberedReleases) { if (hasNumberedReleases) {
const currentReleaseNotes = releaseNotesPerPlan[`${requestedRelease.replace(/\./g, '-')}`] const currentReleaseNotes = releaseNotesPerPlan[`${requestedRelease.replace(/\./g, '-')}`]
if (!currentReleaseNotes && req.path.endsWith('/release-notes')) { if (!currentReleaseNotes && req.pagePath.endsWith('/release-notes')) {
// If the GHES version doesn't have any release notes, let's be helpful and redirect to `enterprise.github.com` // If the GHES version doesn't have any release notes, let's be helpful and redirect to `enterprise.github.com`
return requestedPlan === 'enterprise-server' return requestedPlan === 'enterprise-server'
? res.redirect(`https://enterprise.github.com/releases/${requestedRelease}.0/notes`) ? res.redirect(`https://enterprise.github.com/releases/${requestedRelease}.0/notes`)

View File

@@ -15,10 +15,10 @@ module.exports = function restContext (req, res, next) {
)) ))
// ignore requests to non-REST reference paths // ignore requests to non-REST reference paths
if (!req.path.includes('rest/reference')) return next() if (!req.pagePath.includes('rest/reference')) return next()
// e.g. the `activity` from `/en/rest/reference/activity#events` // e.g. the `activity` from `/en/rest/reference/activity#events`
const category = req.path const category = req.pagePath
.split('rest/reference')[1] .split('rest/reference')[1]
.replace(/^\//, '') // remove leading slash .replace(/^\//, '') // remove leading slash
.split('/')[0] .split('/')[0]

View File

@@ -8,7 +8,7 @@ module.exports = function webhooksContext (req, res, next) {
const currentVersionObj = allVersions[req.context.currentVersion] const currentVersionObj = allVersions[req.context.currentVersion]
// ignore requests to non-webhook reference paths // ignore requests to non-webhook reference paths
// and to versions that don't exist // and to versions that don't exist
if (!req.path.includes('webhook') || !currentVersionObj) { if (!req.pagePath.includes('webhook') || !currentVersionObj) {
return next() return next()
} }

View File

@@ -1,4 +1,6 @@
const cookieSettings = require('../lib/cookie-settings')
module.exports = require('csurf')({ module.exports = require('csurf')({
cookie: require('../lib/cookie-settings'), cookie: cookieSettings,
ignoreMethods: ['GET', 'HEAD', 'OPTIONS'] ignoreMethods: ['GET', 'HEAD', 'OPTIONS']
}) })

View File

@@ -1,11 +1,11 @@
// This middleware uses the request path to find a page in the preloaded context.pages object // This middleware uses the request path to find a page in the preloaded context.pages object
module.exports = async function findPage (req, res, next) { module.exports = async function findPage (req, res, next) {
let page = req.context.pages[req.path] let page = req.context.pages[req.pagePath]
// if this is a localized request that can't be found, try finding an English variant // if this is a localized request that can't be found, try finding an English variant
if (!page && req.language !== 'en') { if (!page && req.language !== 'en') {
const englishPath = req.path.replace(new RegExp(`^/${req.language}`), '/en') const englishPath = req.pagePath.replace(new RegExp(`^/${req.language}`), '/en')
// NOTE the fallback page will have page.languageCode = 'en' // NOTE the fallback page will have page.languageCode = 'en'
page = req.context.pages[englishPath] page = req.context.pages[englishPath]
} }

View File

@@ -0,0 +1,19 @@
module.exports = async function handleNextDataPath (req, res, next) {
if (req.path.startsWith('/_next/data') && req.path.endsWith('.json')) {
// translate a nextjs data request to a page path that the server can use on context
// this is triggered via client-side route tranistions
// example path:
// /_next/data/development/en/free-pro-team%40latest/github/setting-up-and-managing-your-github-user-account.json
const decodedPath = decodeURIComponent(req.path)
const parts = decodedPath.split('/').slice(4)
// free-pro-team@latest should not be included in the page path
if (parts[1] === 'free-pro-team@latest') {
parts.splice(1,1)
}
req.pagePath = '/' + parts.join('/').replace(/.json+$/, '')
} else {
req.pagePath = req.path
}
next()
}

View File

@@ -1,6 +1,26 @@
const express = require('express') const express = require('express')
const instrument = require('../lib/instrument-middleware') const instrument = require('../lib/instrument-middleware')
const haltOnDroppedConnection = require('./halt-on-dropped-connection') const haltOnDroppedConnection = require('./halt-on-dropped-connection')
const abort = require('./abort')
const timeout = require('./timeout')
const morgan = require('morgan')
const datadog = require('./connect-datadog')
const rateLimit = require('./rate-limit')
const cors = require('./cors')
const helmet = require('helmet')
const csp = require('./csp')
const cookieParser = require('./cookie-parser')
const csrf = require('./csrf')
const handleCsrfErrors = require('./handle-csrf-errors')
const compression = require('compression')
const disableCachingOnSafari = require('./disable-caching-on-safari')
const setFastlySurrogateKey = require('./set-fastly-surrogate-key')
const setFastlyCacheHeaders = require('./set-fastly-cache-headers')
const catchBadAcceptLanguage = require('./catch-bad-accept-language')
const reqUtils = require('./req-utils')
const recordRedirect = require('./record-redirect')
const connectSlashes = require('connect-slashes')
const handleErrors = require('./handle-errors')
const { NODE_ENV } = process.env const { NODE_ENV } = process.env
const isDevelopment = NODE_ENV === 'development' const isDevelopment = NODE_ENV === 'development'
@@ -15,57 +35,57 @@ const asyncMiddleware = fn =>
module.exports = function (app) { module.exports = function (app) {
// *** Request connection management *** // *** Request connection management ***
if (!isTest) app.use(require('./timeout')) if (!isTest) app.use(timeout)
app.use(require('./abort')) app.use(abort)
// *** Development tools *** // *** Development tools ***
app.use(require('morgan')('dev', { skip: (req, res) => !isDevelopment })) app.use(morgan('dev', { skip: (req, res) => !isDevelopment }))
if (isDevelopment) app.use(require('./webpack'))
// *** Observability *** // *** Observability ***
if (process.env.DD_API_KEY) { if (process.env.DD_API_KEY) {
app.use(require('./connect-datadog')) app.use(datadog)
} }
// *** Early exits *** // *** Early exits ***
// Don't use the proxy's IP, use the requester's for rate limiting // Don't use the proxy's IP, use the requester's for rate limiting
// See https://expressjs.com/en/guide/behind-proxies.html // See https://expressjs.com/en/guide/behind-proxies.html
app.set('trust proxy', 1) app.set('trust proxy', 1)
app.use(require('./rate-limit')) app.use(rateLimit)
app.use(instrument('./handle-invalid-paths')) app.use(instrument('./handle-invalid-paths'))
app.use(instrument('./handle-next-data-path'))
// *** Security *** // *** Security ***
app.use(require('./cors')) app.use(cors)
app.use(require('helmet')({ app.use(helmet({
// Override referrerPolicy to match the browser's default: "strict-origin-when-cross-origin". // Override referrerPolicy to match the browser's default: "strict-origin-when-cross-origin".
// Helmet now defaults to "no-referrer", which is a problem for our archived assets proxying. // Helmet now defaults to "no-referrer", which is a problem for our archived assets proxying.
referrerPolicy: { referrerPolicy: {
policy: 'strict-origin-when-cross-origin' policy: 'strict-origin-when-cross-origin'
} }
})) }))
app.use(require('./csp')) // Must come after helmet app.use(csp) // Must come after helmet
app.use(require('./cookie-parser')) // Must come before csrf app.use(cookieParser) // Must come before csrf
app.use(express.json()) // Must come before csrf app.use(express.json()) // Must come before csrf
app.use(require('./csrf')) app.use(csrf)
app.use(require('./handle-csrf-errors')) // Must come before regular handle-errors app.use(handleCsrfErrors) // Must come before regular handle-errors
// *** Headers *** // *** Headers ***
app.set('etag', false) // We will manage our own ETags if desired app.set('etag', false) // We will manage our own ETags if desired
app.use(require('compression')()) app.use(compression())
app.use(require('./disable-caching-on-safari')) app.use(disableCachingOnSafari)
app.use(require('./set-fastly-surrogate-key')) app.use(setFastlySurrogateKey)
app.use(require('./catch-bad-accept-language')) app.use(catchBadAcceptLanguage)
// *** Config and context for redirects *** // *** Config and context for redirects ***
app.use(require('./req-utils')) // Must come before record-redirect and events app.use(reqUtils) // Must come before record-redirect and events
app.use(require('./record-redirect')) app.use(recordRedirect)
app.use(instrument('./detect-language')) // Must come before context, breadcrumbs, find-page, handle-errors, homepages app.use(instrument('./detect-language')) // Must come before context, breadcrumbs, find-page, handle-errors, homepages
app.use(asyncMiddleware(instrument('./context'))) // Must come before early-access-*, handle-redirects app.use(asyncMiddleware(instrument('./context'))) // Must come before early-access-*, handle-redirects
app.use(asyncMiddleware(instrument('./contextualizers/short-versions'))) // Support version shorthands app.use(asyncMiddleware(instrument('./contextualizers/short-versions'))) // Support version shorthands
// *** Redirects, 3xx responses *** // *** Redirects, 3xx responses ***
// I ordered these by use frequency // I ordered these by use frequency
app.use(require('connect-slashes')(false)) app.use(connectSlashes(false))
app.use(instrument('./redirects/external')) app.use(instrument('./redirects/external'))
app.use(instrument('./redirects/help-to-docs')) app.use(instrument('./redirects/help-to-docs'))
app.use(instrument('./redirects/language-code-redirects')) // Must come before contextualizers app.use(instrument('./redirects/language-code-redirects')) // Must come before contextualizers
@@ -131,7 +151,7 @@ module.exports = function (app) {
app.use(asyncMiddleware(instrument('./is-next-request'))) app.use(asyncMiddleware(instrument('./is-next-request')))
// *** Headers for pages only *** // *** Headers for pages only ***
app.use(require('./set-fastly-cache-headers')) app.use(setFastlyCacheHeaders)
// handle serving NextJS bundled code (/_next/*) // handle serving NextJS bundled code (/_next/*)
if (process.env.FEATURE_NEXTJS) { if (process.env.FEATURE_NEXTJS) {
@@ -145,5 +165,5 @@ module.exports = function (app) {
app.get('/*', asyncMiddleware(instrument('./render-page'))) app.get('/*', asyncMiddleware(instrument('./render-page')))
// *** Error handling, must go last *** // *** Error handling, must go last ***
app.use(require('./handle-errors')) app.use(handleErrors)
} }

View File

@@ -1,38 +1,10 @@
const pathToRegexp = require('path-to-regexp')
const { productIds } = require('../lib/all-products')
const versionIds = Object.keys(require('../lib/all-versions'))
const { FEATURE_NEXTJS } = process.env; const { FEATURE_NEXTJS } = process.env;
const homePageExp = pathToRegexp('/:locale/:versionId?')
const productPageExp = pathToRegexp('/:locale/:versionId?/:productId')
const subSectionExp = pathToRegexp('/:locale/:versionId?/:productId/:subSection*')
module.exports = function isNextRequest(req, res, next) { module.exports = function isNextRequest(req, res, next) {
req.renderWithNextjs = false; req.renderWithNextjs = false;
if (FEATURE_NEXTJS && !req.path.startsWith('/_next/')) { if (FEATURE_NEXTJS) {
if ('nextjs' in req.query) { req.renderWithNextjs = true;
req.renderWithNextjs = true;
} else {
// Custom path matching to determine if we should render with nextjs
// Remove any query string (?...) and/or fragment identifier (#...)
const { pathname } = new URL(req.originalUrl, "https://docs.github.com");
// Should the current path be rendered by NextJS?
const homePageMatch = homePageExp.exec(pathname)
const productPageMatch = productPageExp.exec(pathname)
const subSectionMatch = subSectionExp.exec(pathname)
if (homePageMatch && (!homePageMatch[2] || versionIds.includes(homePageMatch[2]))) {
req.renderWithNextjs = true
} else if (productPageMatch && productIds.includes(productPageMatch[3])) {
req.renderWithNextjs = true
} else if (subSectionMatch) {
req.renderWithNextjs = true
}
}
} }
next(); next();

View File

@@ -20,7 +20,7 @@ module.exports = async function learningTrack (req, res, next) {
const currentLearningTrack = { trackName } const currentLearningTrack = { trackName }
const guidePath = getPathWithoutLanguage(getPathWithoutVersion(req.path)) const guidePath = getPathWithoutLanguage(getPathWithoutVersion(req.pagePath))
const guideIndex = track.guides.findIndex((path) => path === guidePath) const guideIndex = track.guides.findIndex((path) => path === guidePath)
if (guideIndex < 0) return noTrack() if (guideIndex < 0) return noTrack()

View File

@@ -11,7 +11,7 @@ if (FEATURE_NEXTJS) {
} }
module.exports = function renderPageWithNext (req, res, next) { module.exports = function renderPageWithNext (req, res, next) {
if (req.path.startsWith('/_next/')) { if (req.path.startsWith('/_next') && !req.path.startsWith('/_next/data')) {
return nextHandleRequest(req, res) return nextHandleRequest(req, res)
} }

View File

@@ -131,7 +131,7 @@ module.exports = async function renderPage (req, res, next) {
const context = Object.assign({}, req.context, { page }) const context = Object.assign({}, req.context, { page })
// collect URLs for variants of this page in all languages // collect URLs for variants of this page in all languages
context.page.languageVariants = Page.getLanguageVariants(req.path) context.page.languageVariants = Page.getLanguageVariants(req.pagePath)
// Stop processing if the connection was already dropped // Stop processing if the connection was already dropped
if (isConnectionDropped(req, res)) return if (isConnectionDropped(req, res)) return
@@ -147,14 +147,14 @@ module.exports = async function renderPage (req, res, next) {
} }
// handle special-case prerendered GraphQL objects page // handle special-case prerendered GraphQL objects page
if (req.path.endsWith('graphql/reference/objects')) { if (req.pagePath.endsWith('graphql/reference/objects')) {
// concat the markdown source miniToc items and the prerendered miniToc items // concat the markdown source miniToc items and the prerendered miniToc items
context.miniTocItems = context.miniTocItems.concat(req.context.graphql.prerenderedObjectsForCurrentVersion.miniToc) context.miniTocItems = context.miniTocItems.concat(req.context.graphql.prerenderedObjectsForCurrentVersion.miniToc)
context.renderedPage = context.renderedPage + req.context.graphql.prerenderedObjectsForCurrentVersion.html context.renderedPage = context.renderedPage + req.context.graphql.prerenderedObjectsForCurrentVersion.html
} }
// handle special-case prerendered GraphQL input objects page // handle special-case prerendered GraphQL input objects page
if (req.path.endsWith('graphql/reference/input-objects')) { if (req.pagePath.endsWith('graphql/reference/input-objects')) {
// concat the markdown source miniToc items and the prerendered miniToc items // concat the markdown source miniToc items and the prerendered miniToc items
context.miniTocItems = context.miniTocItems.concat(req.context.graphql.prerenderedInputObjectsForCurrentVersion.miniToc) context.miniTocItems = context.miniTocItems.concat(req.context.graphql.prerenderedInputObjectsForCurrentVersion.miniToc)
context.renderedPage = context.renderedPage + req.context.graphql.prerenderedInputObjectsForCurrentVersion.html context.renderedPage = context.renderedPage + req.context.graphql.prerenderedInputObjectsForCurrentVersion.html
@@ -164,7 +164,7 @@ module.exports = async function renderPage (req, res, next) {
context.page.fullTitle = context.page.titlePlainText context.page.fullTitle = context.page.titlePlainText
// add localized ` - GitHub Docs` suffix to <title> tag (except for the homepage) // add localized ` - GitHub Docs` suffix to <title> tag (except for the homepage)
if (!patterns.homepagePath.test(req.path)) { if (!patterns.homepagePath.test(req.pagePath)) {
context.page.fullTitle = context.page.fullTitle + ' - ' + context.site.data.ui.header.github_docs context.page.fullTitle = context.page.fullTitle + ' - ' + context.site.data.ui.header.github_docs
} }

View File

@@ -1,6 +1,8 @@
const express = require('express') const express = require('express')
const languages = new Set(Object.keys(require('../lib/languages'))) const libLanguages = require('../lib/languages')
const versions = new Set(Object.values(require('../lib/search/versions'))) const searchVersions = require('../lib/search/versions')
const languages = new Set(Object.keys(libLanguages))
const versions = new Set(Object.values(searchVersions))
const loadLunrResults = require('../lib/search/lunr-search') const loadLunrResults = require('../lib/search/lunr-search')
const loadAlgoliaResults = require('../lib/search/algolia-search') const loadAlgoliaResults = require('../lib/search/algolia-search')

View File

@@ -1,14 +0,0 @@
const webpack = require('webpack')
const middleware = require('webpack-dev-middleware')
const config = require('../webpack.config')
const webpackCompiler = webpack({
...config,
plugins: [
...config.plugins
]
})
module.exports = middleware(webpackCompiler, {
publicPath: config.output.publicPath
})

View File

@@ -6,6 +6,7 @@
"translations", "translations",
"dist", "dist",
"javascripts", "javascripts",
"stylesheets" "stylesheets",
"tests"
] ]
} }

22
package-lock.json generated
View File

@@ -51,12 +51,12 @@
"lodash": "^4.17.21", "lodash": "^4.17.21",
"lunr": "^2.3.9", "lunr": "^2.3.9",
"lunr-languages": "^1.4.0", "lunr-languages": "^1.4.0",
"mdast-util-from-markdown": "^0.8.5",
"mdast-util-to-string": "^2.0.0", "mdast-util-to-string": "^2.0.0",
"morgan": "^1.10.0", "morgan": "^1.10.0",
"next": "^11.0.0", "next": "^11.0.0",
"node-fetch": "^2.6.1", "node-fetch": "^2.6.1",
"parse5": "^6.0.1", "parse5": "^6.0.1",
"path-to-regexp": "^0.1.7",
"port-used": "^2.0.8", "port-used": "^2.0.8",
"rate-limit-redis": "^2.1.0", "rate-limit-redis": "^2.1.0",
"react": "^17.0.2", "react": "^17.0.2",
@@ -143,7 +143,6 @@
"jest-slow-test-reporter": "^1.0.0", "jest-slow-test-reporter": "^1.0.0",
"linkinator": "^2.13.6", "linkinator": "^2.13.6",
"make-promises-safe": "^5.1.0", "make-promises-safe": "^5.1.0",
"mdast-util-from-markdown": "^0.8.5",
"mini-css-extract-plugin": "^1.6.0", "mini-css-extract-plugin": "^1.6.0",
"mkdirp": "^1.0.4", "mkdirp": "^1.0.4",
"mock-express-response": "^0.3.0", "mock-express-response": "^0.3.0",
@@ -169,7 +168,6 @@
"url-template": "^2.0.8", "url-template": "^2.0.8",
"webpack": "^5.37.0", "webpack": "^5.37.0",
"webpack-cli": "^4.7.0", "webpack-cli": "^4.7.0",
"webpack-dev-middleware": "^4.2.0",
"website-scraper": "^4.2.3" "website-scraper": "^4.2.3"
}, },
"engines": { "engines": {
@@ -4179,7 +4177,6 @@
"version": "3.0.3", "version": "3.0.3",
"resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-3.0.3.tgz", "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-3.0.3.tgz",
"integrity": "sha512-SXPBMnFVQg1s00dlMCc/jCdvPqdE4mXaMMCeRlxLDmTAEoegHT53xKtkDnzDTOcmMHUfcjyf36/YYZ6SxRdnsw==", "integrity": "sha512-SXPBMnFVQg1s00dlMCc/jCdvPqdE4mXaMMCeRlxLDmTAEoegHT53xKtkDnzDTOcmMHUfcjyf36/YYZ6SxRdnsw==",
"dev": true,
"dependencies": { "dependencies": {
"@types/unist": "*" "@types/unist": "*"
} }
@@ -16037,7 +16034,6 @@
"version": "0.8.5", "version": "0.8.5",
"resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-0.8.5.tgz", "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-0.8.5.tgz",
"integrity": "sha512-2hkTXtYYnr+NubD/g6KGBS/0mFmBcifAsI0yIWRiRo0PjVs6SSOSOdtzbp6kSGnShDN6G5aWZpKQ2lWRy27mWQ==", "integrity": "sha512-2hkTXtYYnr+NubD/g6KGBS/0mFmBcifAsI0yIWRiRo0PjVs6SSOSOdtzbp6kSGnShDN6G5aWZpKQ2lWRy27mWQ==",
"dev": true,
"dependencies": { "dependencies": {
"@types/mdast": "^3.0.0", "@types/mdast": "^3.0.0",
"mdast-util-to-string": "^2.0.0", "mdast-util-to-string": "^2.0.0",
@@ -16050,7 +16046,6 @@
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-2.0.0.tgz", "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-2.0.0.tgz",
"integrity": "sha512-kkywGpCcRYhqQIchaWqZ875wzpS/bMKhz5HnN3p7wveJTkTtyAB/AlnS0f8DFSqYW1T82t6yEAkEcB+A1I3MbQ==", "integrity": "sha512-kkywGpCcRYhqQIchaWqZ875wzpS/bMKhz5HnN3p7wveJTkTtyAB/AlnS0f8DFSqYW1T82t6yEAkEcB+A1I3MbQ==",
"dev": true,
"dependencies": { "dependencies": {
"character-entities": "^1.0.0", "character-entities": "^1.0.0",
"character-entities-legacy": "^1.0.0", "character-entities-legacy": "^1.0.0",
@@ -16261,7 +16256,6 @@
"version": "2.11.3", "version": "2.11.3",
"resolved": "https://registry.npmjs.org/micromark/-/micromark-2.11.3.tgz", "resolved": "https://registry.npmjs.org/micromark/-/micromark-2.11.3.tgz",
"integrity": "sha512-oph5YYkVqR2U9OtWBcXYysZMtrdIvi8dfSeyEdr1wFr3Bk6YwI6THosX2AzKnhdps7mVUbXiqhmosu9DcA+xlQ==", "integrity": "sha512-oph5YYkVqR2U9OtWBcXYysZMtrdIvi8dfSeyEdr1wFr3Bk6YwI6THosX2AzKnhdps7mVUbXiqhmosu9DcA+xlQ==",
"dev": true,
"dependencies": { "dependencies": {
"debug": "^4.0.0", "debug": "^4.0.0",
"parse-entities": "^2.0.0" "parse-entities": "^2.0.0"
@@ -16271,7 +16265,6 @@
"version": "4.3.1", "version": "4.3.1",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz",
"integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==",
"dev": true,
"dependencies": { "dependencies": {
"ms": "2.1.2" "ms": "2.1.2"
}, },
@@ -16282,14 +16275,12 @@
"node_modules/micromark/node_modules/ms": { "node_modules/micromark/node_modules/ms": {
"version": "2.1.2", "version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
"dev": true
}, },
"node_modules/micromark/node_modules/parse-entities": { "node_modules/micromark/node_modules/parse-entities": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-2.0.0.tgz", "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-2.0.0.tgz",
"integrity": "sha512-kkywGpCcRYhqQIchaWqZ875wzpS/bMKhz5HnN3p7wveJTkTtyAB/AlnS0f8DFSqYW1T82t6yEAkEcB+A1I3MbQ==", "integrity": "sha512-kkywGpCcRYhqQIchaWqZ875wzpS/bMKhz5HnN3p7wveJTkTtyAB/AlnS0f8DFSqYW1T82t6yEAkEcB+A1I3MbQ==",
"dev": true,
"dependencies": { "dependencies": {
"character-entities": "^1.0.0", "character-entities": "^1.0.0",
"character-entities-legacy": "^1.0.0", "character-entities-legacy": "^1.0.0",
@@ -28366,7 +28357,6 @@
"version": "3.0.3", "version": "3.0.3",
"resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-3.0.3.tgz", "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-3.0.3.tgz",
"integrity": "sha512-SXPBMnFVQg1s00dlMCc/jCdvPqdE4mXaMMCeRlxLDmTAEoegHT53xKtkDnzDTOcmMHUfcjyf36/YYZ6SxRdnsw==", "integrity": "sha512-SXPBMnFVQg1s00dlMCc/jCdvPqdE4mXaMMCeRlxLDmTAEoegHT53xKtkDnzDTOcmMHUfcjyf36/YYZ6SxRdnsw==",
"dev": true,
"requires": { "requires": {
"@types/unist": "*" "@types/unist": "*"
} }
@@ -38331,7 +38321,6 @@
"version": "0.8.5", "version": "0.8.5",
"resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-0.8.5.tgz", "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-0.8.5.tgz",
"integrity": "sha512-2hkTXtYYnr+NubD/g6KGBS/0mFmBcifAsI0yIWRiRo0PjVs6SSOSOdtzbp6kSGnShDN6G5aWZpKQ2lWRy27mWQ==", "integrity": "sha512-2hkTXtYYnr+NubD/g6KGBS/0mFmBcifAsI0yIWRiRo0PjVs6SSOSOdtzbp6kSGnShDN6G5aWZpKQ2lWRy27mWQ==",
"dev": true,
"requires": { "requires": {
"@types/mdast": "^3.0.0", "@types/mdast": "^3.0.0",
"mdast-util-to-string": "^2.0.0", "mdast-util-to-string": "^2.0.0",
@@ -38344,7 +38333,6 @@
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-2.0.0.tgz", "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-2.0.0.tgz",
"integrity": "sha512-kkywGpCcRYhqQIchaWqZ875wzpS/bMKhz5HnN3p7wveJTkTtyAB/AlnS0f8DFSqYW1T82t6yEAkEcB+A1I3MbQ==", "integrity": "sha512-kkywGpCcRYhqQIchaWqZ875wzpS/bMKhz5HnN3p7wveJTkTtyAB/AlnS0f8DFSqYW1T82t6yEAkEcB+A1I3MbQ==",
"dev": true,
"requires": { "requires": {
"character-entities": "^1.0.0", "character-entities": "^1.0.0",
"character-entities-legacy": "^1.0.0", "character-entities-legacy": "^1.0.0",
@@ -38535,7 +38523,6 @@
"version": "2.11.3", "version": "2.11.3",
"resolved": "https://registry.npmjs.org/micromark/-/micromark-2.11.3.tgz", "resolved": "https://registry.npmjs.org/micromark/-/micromark-2.11.3.tgz",
"integrity": "sha512-oph5YYkVqR2U9OtWBcXYysZMtrdIvi8dfSeyEdr1wFr3Bk6YwI6THosX2AzKnhdps7mVUbXiqhmosu9DcA+xlQ==", "integrity": "sha512-oph5YYkVqR2U9OtWBcXYysZMtrdIvi8dfSeyEdr1wFr3Bk6YwI6THosX2AzKnhdps7mVUbXiqhmosu9DcA+xlQ==",
"dev": true,
"requires": { "requires": {
"debug": "^4.0.0", "debug": "^4.0.0",
"parse-entities": "^2.0.0" "parse-entities": "^2.0.0"
@@ -38545,7 +38532,6 @@
"version": "4.3.1", "version": "4.3.1",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz",
"integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==",
"dev": true,
"requires": { "requires": {
"ms": "2.1.2" "ms": "2.1.2"
} }
@@ -38553,14 +38539,12 @@
"ms": { "ms": {
"version": "2.1.2", "version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
"dev": true
}, },
"parse-entities": { "parse-entities": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-2.0.0.tgz", "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-2.0.0.tgz",
"integrity": "sha512-kkywGpCcRYhqQIchaWqZ875wzpS/bMKhz5HnN3p7wveJTkTtyAB/AlnS0f8DFSqYW1T82t6yEAkEcB+A1I3MbQ==", "integrity": "sha512-kkywGpCcRYhqQIchaWqZ875wzpS/bMKhz5HnN3p7wveJTkTtyAB/AlnS0f8DFSqYW1T82t6yEAkEcB+A1I3MbQ==",
"dev": true,
"requires": { "requires": {
"character-entities": "^1.0.0", "character-entities": "^1.0.0",
"character-entities-legacy": "^1.0.0", "character-entities-legacy": "^1.0.0",

View File

@@ -57,13 +57,12 @@
"lodash": "^4.17.21", "lodash": "^4.17.21",
"lunr": "^2.3.9", "lunr": "^2.3.9",
"lunr-languages": "^1.4.0", "lunr-languages": "^1.4.0",
"mdast-util-to-string": "^2.0.0",
"mdast-util-from-markdown": "^0.8.5", "mdast-util-from-markdown": "^0.8.5",
"mdast-util-to-string": "^2.0.0",
"morgan": "^1.10.0", "morgan": "^1.10.0",
"next": "^11.0.0", "next": "^11.0.0",
"node-fetch": "^2.6.1", "node-fetch": "^2.6.1",
"parse5": "^6.0.1", "parse5": "^6.0.1",
"path-to-regexp": "^0.1.7",
"port-used": "^2.0.8", "port-used": "^2.0.8",
"rate-limit-redis": "^2.1.0", "rate-limit-redis": "^2.1.0",
"react": "^17.0.2", "react": "^17.0.2",
@@ -175,7 +174,6 @@
"url-template": "^2.0.8", "url-template": "^2.0.8",
"webpack": "^5.37.0", "webpack": "^5.37.0",
"webpack-cli": "^4.7.0", "webpack-cli": "^4.7.0",
"webpack-dev-middleware": "^4.2.0",
"website-scraper": "^4.2.3" "website-scraper": "^4.2.3"
}, },
"optionalDependencies": { "optionalDependencies": {

View File

@@ -11,10 +11,8 @@ import events from 'javascripts/events'
import experiment from 'javascripts/experiment' import experiment from 'javascripts/experiment'
import setNextEnv from 'javascripts/set-next-env' import setNextEnv from 'javascripts/set-next-env'
type MyAppProps = AppProps & { csrfToken: string; themeProps: typeof defaultThemeProps } type MyAppProps = AppProps & { csrfToken: string; themeProps: typeof defaultThemeProps }
const MyApp = ({ Component, pageProps, csrfToken, themeProps }: MyAppProps) => { const MyApp = ({ Component, pageProps, csrfToken, themeProps }: MyAppProps) => {
useEffect(() => { useEffect(() => {
events() events()
experiment() experiment()

View File

@@ -44,7 +44,7 @@ type LandingPageProps = {
function LandingPage(props: LandingPageProps) { function LandingPage(props: LandingPageProps) {
const router = useRouter() const router = useRouter()
const { gettingStartedLinks, popularLinks } = props const { gettingStartedLinks, popularLinks } = props
const { activeProducts, isHomepageVersion } = useMainContext() const { activeProducts } = useMainContext()
const { currentVersion } = useVersion() const { currentVersion } = useVersion()
const { t } = useTranslation(['homepage', 'search', 'toc']) const { t } = useTranslation(['homepage', 'search', 'toc'])
return ( return (
@@ -80,7 +80,7 @@ function LandingPage(props: LandingPageProps) {
</h2> </h2>
<div className="d-flex flex-wrap gutter gutter-xl-spacious"> <div className="d-flex flex-wrap gutter gutter-xl-spacious">
{activeProducts.map((product) => { {activeProducts.map((product) => {
if (!product.versions?.includes(currentVersion) && !isHomepageVersion) { if (!product.versions?.includes(currentVersion) && !product.external) {
return null return null
} }

View File

@@ -63,7 +63,7 @@ async function main () {
fs.writeFileSync(newDereferencedFile, newDereferenceContent) fs.writeFileSync(newDereferencedFile, newDereferenceContent)
console.log(`Created ${newDereferencedFile}.`) console.log(`Created ${newDereferencedFile}.`)
const dereferencedSchema = require(path.join(process.cwd(), newDereferencedFile)) const dereferencedSchema = JSON.parse(fs.readFileSync(path.join(process.cwd(), newDereferencedFile)))
// Store all operations in an array of operation objects // Store all operations in an array of operation objects
const operations = await getOperations(dereferencedSchema) const operations = await getOperations(dereferencedSchema)

View File

@@ -1,5 +1,7 @@
const dotenv = require('dotenv')
if (!process.env.GITHUB_TOKEN) { if (!process.env.GITHUB_TOKEN) {
require('dotenv').config() dotenv.config()
} }
// this module needs to work in development, production, and GitHub Actions // this module needs to work in development, production, and GitHub Actions

View File

@@ -3,8 +3,10 @@
const fs = require('fs') const fs = require('fs')
const path = require('path') const path = require('path')
const walk = require('walk-sync') const walk = require('walk-sync')
const slugger = new (require('github-slugger'))() const GithubSlugger = require('github-slugger')
const entities = new (require('html-entities').XmlEntities)() const htmlEntities = require('html-entities')
const slugger = new GithubSlugger()
const entities = new htmlEntities.XmlEntities()
const frontmatter = require('../lib/read-frontmatter') const frontmatter = require('../lib/read-frontmatter')
const { execSync } = require('child_process') const { execSync } = require('child_process')
const addRedirectToFrontmatter = require('./helpers/add-redirect-to-frontmatter') const addRedirectToFrontmatter = require('./helpers/add-redirect-to-frontmatter')

View File

@@ -1,5 +1,6 @@
#!/usr/bin/env node #!/usr/bin/env node
const fs = require('fs')
const path = require('path')
const glob = require('glob') const glob = require('glob')
const program = require('commander') const program = require('commander')
const getOperations = require('./utils/get-operations') const getOperations = require('./utils/get-operations')
@@ -28,8 +29,7 @@ if (filesToCheck.length) {
async function check (files) { async function check (files) {
console.log('Verifying OpenAPI files are valid with decorator') console.log('Verifying OpenAPI files are valid with decorator')
const documents = files.map(filename => [filename, JSON.parse(fs.readFileSync(path.join(process.cwd(), filename)))])
const documents = files.map(filename => [filename, require(filename)])
for (const [filename, schema] of documents) { for (const [filename, schema] of documents) {
try { try {

View File

@@ -75,7 +75,7 @@ async function getDereferencedFiles () {
// name of the `github/github` checkout. A CI test // name of the `github/github` checkout. A CI test
// checks the version and fails if it's not a semantic version. // checks the version and fails if it's not a semantic version.
schemas.forEach(filename => { schemas.forEach(filename => {
const schema = require(path.join(dereferencedPath, filename)) const schema = JSON.parse(fs.readFileSync(path.join(dereferencedPath, filename)))
schema.info.version = `${githubBranch} !!DEVELOPMENT MODE - DO NOT MERGE!!` schema.info.version = `${githubBranch} !!DEVELOPMENT MODE - DO NOT MERGE!!`
fs.writeFileSync(path.join(dereferencedPath, filename), JSON.stringify(schema, null, 2)) fs.writeFileSync(path.join(dereferencedPath, filename), JSON.stringify(schema, null, 2))
}) })
@@ -85,7 +85,7 @@ async function decorate () {
console.log('\n🎄 Decorating the OpenAPI schema files in lib/rest/static/dereferenced.\n') console.log('\n🎄 Decorating the OpenAPI schema files in lib/rest/static/dereferenced.\n')
const dereferencedSchemas = schemas.reduce((acc, filename) => { const dereferencedSchemas = schemas.reduce((acc, filename) => {
const schema = require(path.join(dereferencedPath, filename)) const schema = JSON.parse(fs.readFileSync(path.join(dereferencedPath, filename)))
const key = filename.replace('.deref.json', '') const key = filename.replace('.deref.json', '')
return { ...acc, [key]: schema } return { ...acc, [key]: schema }
}, {}) }, {})

View File

@@ -1,10 +1,12 @@
const { get, flatten, isPlainObject } = require('lodash') const { get, flatten, isPlainObject } = require('lodash')
const { sentenceCase } = require('change-case') const { sentenceCase } = require('change-case')
const slugger = new (require('github-slugger'))() const GitHubSlugger = require('github-slugger')
const slugger = new GitHubSlugger()
const httpStatusCodes = require('http-status-code') const httpStatusCodes = require('http-status-code')
const renderContent = require('../../../lib/render-content') const renderContent = require('../../../lib/render-content')
const createCodeSamples = require('./create-code-samples') const createCodeSamples = require('./create-code-samples')
const Ajv = require('ajv') const Ajv = require('ajv')
const operationSchema = require('./operation-schema')
// titles that can't be derived by sentence-casing the ID // titles that can't be derived by sentence-casing the ID
const categoryTitles = { scim: 'SCIM' } const categoryTitles = { scim: 'SCIM' }
@@ -39,7 +41,7 @@ module.exports = class Operation {
} }
get schema () { get schema () {
return require('./operation-schema') return operationSchema
} }
async process () { async process () {

View File

@@ -6,13 +6,13 @@
// It can also be run manually. For more info see [contributing/search.md](contributing/search.md) // It can also be run manually. For more info see [contributing/search.md](contributing/search.md)
// //
// [end-readme] // [end-readme]
const searchSync = require('./search/sync')
require('make-promises-safe') require('make-promises-safe')
main() main()
async function main () { async function main () {
const sync = require('./search/sync') const sync = searchSync
const opts = { const opts = {
dryRun: 'DRY_RUN' in process.env, dryRun: 'DRY_RUN' in process.env,
language: process.env.LANGUAGE, language: process.env.LANGUAGE,

View File

@@ -4,6 +4,9 @@ const throng = require('throng')
const os = require('os') const os = require('os')
const portUsed = require('port-used') const portUsed = require('port-used')
const prefixStreamWrite = require('./lib/prefix-stream-write') const prefixStreamWrite = require('./lib/prefix-stream-write')
const libApp = require('./lib/app')
const libWarmServer = require('./lib/warm-server')
const http = require('http')
// Intentionally require these for both cluster primary and workers // Intentionally require these for both cluster primary and workers
require('./lib/check-node-version') require('./lib/check-node-version')
@@ -46,8 +49,8 @@ async function checkPortAvailability () {
} }
async function startServer () { async function startServer () {
const app = require('./lib/app') const app = libApp
const warmServer = require('./lib/warm-server') const warmServer = libWarmServer
// If in a deployed environment... // If in a deployed environment...
if (NODE_ENV === 'production') { if (NODE_ENV === 'production') {
@@ -58,7 +61,7 @@ async function startServer () {
} }
// Workaround for https://github.com/expressjs/express/issues/1101 // Workaround for https://github.com/expressjs/express/issues/1101
const server = require('http').createServer(app) const server = http.createServer(app)
server server
.listen(port, () => console.log(`app running on http://localhost:${port}`)) .listen(port, () => console.log(`app running on http://localhost:${port}`))
.on('error', () => server.close()) .on('error', () => server.close())

View File

@@ -1,7 +1,10 @@
/* global page, browser */ /* global page, browser */
const fs = require('fs')
const path = require('path')
const sleep = require('await-sleep') const sleep = require('await-sleep')
const { latest } = require('../../lib/enterprise-server-releases') const { latest } = require('../../lib/enterprise-server-releases')
const languages = require('../../lib/languages') const languages = require('../../lib/languages')
const featureFlags = JSON.parse(fs.readFileSync(path.join(process.cwd(), '/feature-flags.json')))
describe('homepage', () => { describe('homepage', () => {
jest.setTimeout(60 * 1000) jest.setTimeout(60 * 1000)
@@ -329,7 +332,7 @@ describe('nextjs query param', () => {
jest.setTimeout(60 * 1000) jest.setTimeout(60 * 1000)
it('landing page renders through nextjs pipeline depending on FEATURE_NEXTJS value', async () => { it('landing page renders through nextjs pipeline depending on FEATURE_NEXTJS value', async () => {
const flagVal = require('../../feature-flags.json').FEATURE_NEXTJS const flagVal = featureFlags.FEATURE_NEXTJS
await page.goto('http://localhost:4001/en/actions?nextjs=') await page.goto('http://localhost:4001/en/actions?nextjs=')
const IS_NEXTJS_PAGE = await page.evaluate(() => window.IS_NEXTJS_PAGE) const IS_NEXTJS_PAGE = await page.evaluate(() => window.IS_NEXTJS_PAGE)
const nextWrapper = await page.$('#__next') const nextWrapper = await page.$('#__next')
@@ -337,3 +340,25 @@ describe('nextjs query param', () => {
flagVal === true ? expect(IS_NEXTJS_PAGE).toBe(true) : expect(IS_NEXTJS_PAGE).toBe(false) flagVal === true ? expect(IS_NEXTJS_PAGE).toBe(true) : expect(IS_NEXTJS_PAGE).toBe(false)
}) })
}) })
describe('next/link client-side navigation', () => {
jest.setTimeout(60 * 1000)
it('should have 200 response to /_next/data when link is clicked', async () => {
const initialViewport = page.viewport()
await page.setViewport({ width: 1024, height: 768 })
await page.goto('http://localhost:4001/en/actions')
const [response] = await Promise.all([
page.waitForResponse(
(response) =>
response.url().startsWith('http://localhost:4001/_next/data')
),
page.waitForNavigation({ waitUntil: 'networkidle2' }),
page.click('.sidebar-categories .sidebar-category:nth-child(2) a'),
])
expect(response.status()).toBe(200)
await page.setViewport(initialViewport)
})
})

View File

@@ -1,3 +1,5 @@
const fs = require('fs')
const path = require('path')
const previewsJson = require('../../lib/graphql/static/previews') const previewsJson = require('../../lib/graphql/static/previews')
const upcomingChangesJson = require('../../lib/graphql/static/upcoming-changes') const upcomingChangesJson = require('../../lib/graphql/static/upcoming-changes')
const prerenderedObjectsJson = require('../../lib/graphql/static/prerendered-objects') const prerenderedObjectsJson = require('../../lib/graphql/static/prerendered-objects')
@@ -20,7 +22,7 @@ describe('graphql json files', () => {
test('schemas object validation', () => { test('schemas object validation', () => {
graphqlVersions.forEach(version => { graphqlVersions.forEach(version => {
const schemaJsonPerVersion = require(`../../lib/graphql/static/schema-${version}`) const schemaJsonPerVersion = JSON.parse(fs.readFileSync(path.join(process.cwd(), `lib/graphql/static/schema-${version}.json`)))
// all graphql types are arrays except for queries // all graphql types are arrays except for queries
graphqlTypes graphqlTypes
.filter(type => type !== 'queries') .filter(type => type !== 'queries')

View File

@@ -5,6 +5,7 @@ const flat = require('flat')
const loadSiteData = require('../../lib/site-data') const loadSiteData = require('../../lib/site-data')
const patterns = require('../../lib/patterns') const patterns = require('../../lib/patterns')
const { liquid } = require('../../lib/render-content') const { liquid } = require('../../lib/render-content')
const walkSync = require('walk-sync')
describe('siteData module (English)', () => { describe('siteData module (English)', () => {
let data let data
@@ -82,7 +83,7 @@ describe('siteData module (English)', () => {
}) })
test('warn if any YAML reusables are found', async () => { test('warn if any YAML reusables are found', async () => {
const reusables = require('walk-sync')(path.join(__dirname, '../../data/reusables')) const reusables = walkSync(path.join(__dirname, '../../data/reusables'))
expect(reusables.length).toBeGreaterThan(100) expect(reusables.length).toBeGreaterThan(100)
const yamlReusables = reusables.filter(filename => filename.endsWith('.yml') || filename.endsWith('.yaml')) const yamlReusables = reusables.filter(filename => filename.endsWith('.yml') || filename.endsWith('.yaml'))
const message = `reusables are now written as individual Markdown files. Please migrate the following YAML files to Markdown:\n${yamlReusables.join('\n')}` const message = `reusables are now written as individual Markdown files. Please migrate the following YAML files to Markdown:\n${yamlReusables.join('\n')}`

View File

@@ -3,6 +3,7 @@ const path = require('path')
const { difference, isPlainObject } = require('lodash') const { difference, isPlainObject } = require('lodash')
const { getJSON } = require('../helpers/supertest') const { getJSON } = require('../helpers/supertest')
const enterpriseServerReleases = require('../../lib/enterprise-server-releases') const enterpriseServerReleases = require('../../lib/enterprise-server-releases')
const rest = require('../../lib/rest')
// list of REST markdown files that do not correspond to REST API resources // list of REST markdown files that do not correspond to REST API resources
// TODO could we get this list dynamically, say via page frontmatter? // TODO could we get this list dynamically, say via page frontmatter?
const excludeFromResourceNameCheck = [ const excludeFromResourceNameCheck = [
@@ -15,7 +16,7 @@ describe('REST references docs', () => {
jest.setTimeout(3 * 60 * 1000) jest.setTimeout(3 * 60 * 1000)
test('markdown file exists for every operationId prefix in the api.github.com schema', async () => { test('markdown file exists for every operationId prefix in the api.github.com schema', async () => {
const { categories } = require('../../lib/rest') const { categories } = rest
const referenceDir = path.join(__dirname, '../../content/rest/reference') const referenceDir = path.join(__dirname, '../../content/rest/reference')
const filenames = (await fs.readdir(referenceDir)) const filenames = (await fs.readdir(referenceDir))
.filter(filename => !excludeFromResourceNameCheck.find(excludedFile => filename.endsWith(excludedFile))) .filter(filename => !excludeFromResourceNameCheck.find(excludedFile => filename.endsWith(excludedFile)))
@@ -47,7 +48,7 @@ describe('REST references docs', () => {
}) })
test('no wrongly detected AppleScript syntax highlighting in schema data', async () => { test('no wrongly detected AppleScript syntax highlighting in schema data', async () => {
const { operations } = require('../../lib/rest') const { operations } = rest
expect(JSON.stringify(operations).includes('hljs language-applescript')).toBe(false) expect(JSON.stringify(operations).includes('hljs language-applescript')).toBe(false)
}) })
}) })

View File

@@ -6,6 +6,7 @@ const path = require('path')
const { loadPages } = require('../../lib/page-data') const { loadPages } = require('../../lib/page-data')
const builtAssets = require('../../lib/built-asset-urls') const builtAssets = require('../../lib/built-asset-urls')
const AZURE_STORAGE_URL = 'githubdocs.azureedge.net' const AZURE_STORAGE_URL = 'githubdocs.azureedge.net'
const CspParse = require('csp-parse')
describe('server', () => { describe('server', () => {
jest.setTimeout(60 * 1000) jest.setTimeout(60 * 1000)
@@ -39,7 +40,7 @@ describe('server', () => {
const res = await get('/en') const res = await get('/en')
expect('content-security-policy' in res.headers).toBe(true) expect('content-security-policy' in res.headers).toBe(true)
const csp = new (require('csp-parse'))(res.headers['content-security-policy']) const csp = new CspParse(res.headers['content-security-policy'])
expect(csp.get('default-src')).toBe("'none'") expect(csp.get('default-src')).toBe("'none'")
expect(csp.get('font-src').includes("'self'")).toBe(true) expect(csp.get('font-src').includes("'self'")).toBe(true)

View File

@@ -1,4 +1,7 @@
const fs = require('fs')
const path = require('path')
const { head } = require('../helpers/supertest') const { head } = require('../helpers/supertest')
const topOldDeveloperSitePaths = JSON.parse(fs.readFileSync(path.join(process.cwd(), 'tests/fixtures/top-old-developer-site-paths.json')))
describe('developer.github.com redirects', () => { describe('developer.github.com redirects', () => {
jest.setTimeout(30 * 60 * 1000) jest.setTimeout(30 * 60 * 1000)
@@ -16,7 +19,7 @@ describe('developer.github.com redirects', () => {
// test a subset of the top paths // test a subset of the top paths
const pathsToCheck = 50 const pathsToCheck = 50
const paths = require('../fixtures/top-old-developer-site-paths.json') const paths = topOldDeveloperSitePaths
.filter(path => !ignoredPatterns.some(pattern => path.match(pattern))) .filter(path => !ignoredPatterns.some(pattern => path.match(pattern)))
.slice(0, pathsToCheck) .slice(0, pathsToCheck)

View File

@@ -1,6 +1,8 @@
const fs = require('fs').promises const fs = require('fs').promises
const path = require('path') const path = require('path')
const { testViaActionsOnly } = require('../helpers/conditional-runs') const { testViaActionsOnly } = require('../helpers/conditional-runs')
const { getDOM } = require('../helpers/supertest')
const got = require('got')
describe('cloning early-access', () => { describe('cloning early-access', () => {
testViaActionsOnly('the content directory exists', async () => { testViaActionsOnly('the content directory exists', async () => {
@@ -18,3 +20,25 @@ describe('cloning early-access', () => {
expect(await fs.stat(eaDir)).toBeTruthy() expect(await fs.stat(eaDir)).toBeTruthy()
}) })
}) })
describe('rendering early-access', () => {
jest.setTimeout(5 * 60 * 1000)
testViaActionsOnly('the top-level TOC renders locally', async () => {
const $ = await getDOM('/en/early-access')
expect($.html().includes('Hello, local developer! This page is not visible on production.')).toBe(true)
expect($('ul a').length).toBeGreaterThan(5)
})
testViaActionsOnly('the top-level TOC does not render on production', async () => {
async function getEarlyAccess () {
return await got('https://docs.github.com/en/early-access')
}
await expect(getEarlyAccess).rejects.toThrowError('Response code 404 (Not Found)')
})
testViaActionsOnly('TOCs display on category pages', async () => {
const $ = await getDOM('/en/early-access/github/enforcing-best-practices-with-github-policies')
expect($('ul a').length).toBeGreaterThan(5)
})
})

View File

@@ -1,6 +1,7 @@
const { liquid } = require('../../lib/render-content') const { liquid } = require('../../lib/render-content')
const { loadPageMap } = require('../../lib/page-data') const { loadPageMap } = require('../../lib/page-data')
const entities = new (require('html-entities').XmlEntities)() const htmlEntities = require('html-entities')
const entities = new htmlEntities.XmlEntities()
const nonEnterpriseDefaultVersion = require('../../lib/non-enterprise-default-version') const nonEnterpriseDefaultVersion = require('../../lib/non-enterprise-default-version')
describe('liquid helper tags', () => { describe('liquid helper tags', () => {

View File

@@ -268,7 +268,7 @@ describe('Page class', () => {
basePath: path.join(__dirname, '../../content'), basePath: path.join(__dirname, '../../content'),
languageCode: 'en' languageCode: 'en'
}) })
expect(page.permalinks.find(permalink => permalink.pageVersion === 'homepage').href).toBe('/en') expect(page.permalinks.find(permalink => permalink.pageVersion === nonEnterpriseDefaultVersion).href).toBe('/en')
expect(page.permalinks.find(permalink => permalink.pageVersion === `enterprise-server@${enterpriseServerReleases.oldestSupported}`).href).toBe(`/en/enterprise-server@${enterpriseServerReleases.oldestSupported}`) expect(page.permalinks.find(permalink => permalink.pageVersion === `enterprise-server@${enterpriseServerReleases.oldestSupported}`).href).toBe(`/en/enterprise-server@${enterpriseServerReleases.oldestSupported}`)
}) })

View File

@@ -7,7 +7,6 @@ const getApplicableVersions = require('../../lib/get-applicable-versions')
// Permalink.derive requires: languageCode, relativePath, title, versions (<- FM prop) // Permalink.derive requires: languageCode, relativePath, title, versions (<- FM prop)
describe('Permalink class', () => { describe('Permalink class', () => {
// We can only use Permalink.derive to get the special 'homepage' permalink
test('derives info for unversioned homepage', () => { test('derives info for unversioned homepage', () => {
const versions = { const versions = {
'free-pro-team': '*', 'free-pro-team': '*',
@@ -15,7 +14,7 @@ describe('Permalink class', () => {
} }
const permalinks = Permalink.derive('en', 'index.md', 'Hello World', getApplicableVersions(versions)) const permalinks = Permalink.derive('en', 'index.md', 'Hello World', getApplicableVersions(versions))
expect(permalinks.length).toBeGreaterThan(1) expect(permalinks.length).toBeGreaterThan(1)
const homepagePermalink = permalinks.find(permalink => permalink.pageVersion === 'homepage') const homepagePermalink = permalinks.find(permalink => permalink.pageVersion === nonEnterpriseDefaultVersion)
expect(homepagePermalink.href).toBe('/en') expect(homepagePermalink.href).toBe('/en')
}) })

View File

@@ -30,10 +30,10 @@ describe('versions middleware', () => {
test('adds res.context.currentVersion string', async () => { test('adds res.context.currentVersion string', async () => {
let currentVersion = await getJSON('/en?json=currentVersion') let currentVersion = await getJSON('/en?json=currentVersion')
expect(currentVersion).toBe('homepage') expect(currentVersion).toBe(nonEnterpriseDefaultVersion)
currentVersion = await getJSON(`/en/${nonEnterpriseDefaultVersion}?json=currentVersion`) currentVersion = await getJSON(`/en/${nonEnterpriseDefaultVersion}?json=currentVersion`)
expect(currentVersion).toBe('homepage') expect(currentVersion).toBe(nonEnterpriseDefaultVersion)
currentVersion = await getJSON(`/en/enterprise-server@${latest}?json=currentVersion`) currentVersion = await getJSON(`/en/enterprise-server@${latest}?json=currentVersion`)
expect(currentVersion).toBe(`enterprise-server@${latest}`) expect(currentVersion).toBe(`enterprise-server@${latest}`)