1
0
mirror of synced 2026-01-03 15:05:54 -05:00

Merge branch 'main' into patch-1

This commit is contained in:
Courtney Wilson
2022-11-14 10:14:59 -06:00
committed by GitHub
38 changed files with 558 additions and 148 deletions

View File

@@ -95,10 +95,9 @@ jobs:
run: |
mkdir /tmp/records
npm run sync-search-indices -- \
npm run sync-search-indices -- /tmp/records \
--language en \
--version dotcom \
/tmp/records
--version dotcom
ls -lh /tmp/records
@@ -108,10 +107,9 @@ jobs:
- name: Index into Elasticsearch
run: |
./script/search/index-elasticsearch.js \
./script/search/index-elasticsearch.js /tmp/records \
--language en \
--version dotcom \
/tmp/records
--version dotcom
- name: Check created indexes and aliases
run: |

View File

@@ -16,24 +16,33 @@ import { Link } from 'components/Link'
import styles from './Search.module.scss'
// The search endpoint used prior to using /api/search/legacy was
// just /search, which used middleware/search.js. We are leaving that
// middleware in tact to allow folks that previously used the /search
// endpoint to continue doing so. But, we changed the endpoint used by
// the search input on docs.github.com to use the new /api/search/legacy
// endpoint.
// Eventually, we will deprecate the /search and /api/search/legacy
// endpoints and use the /api/search/v1 endpoint, which has
// a different response JSON format.
const SEARCH_API_ENDPOINT = '/api/search/legacy'
const SEARCH_API_ENDPOINT = '/api/search/v1'
type SearchResult = {
type Hit = {
id: string
url: string
breadcrumbs: string
title: string
content: string
score: number
popularity: number
breadcrumbs: string
highlights: {
title: Array<string>
content: Array<string>
}
score?: number
popularity?: number
es_url?: string
}
// Note, the JSON will contain other keys too, but since we don't use
// them in this component, we don't need to specify them.
type Meta = {
found: {
value: number
}
}
type Data = {
hits: Hit[]
meta: Meta
}
type Props = {
@@ -73,10 +82,14 @@ export function Search({
language,
version,
query,
// In its current state, can't with confidence know if the user
// initiated a search because of the input debounce or if the
// user has finished typing.
autocomplete: 'true',
})}`
: null
const { data: results, error: searchError } = useSWR<SearchResult[], Error>(
const { data, error: searchError } = useSWR<Data, Error>(
fetchURL,
async (url: string) => {
const response = await fetch(url)
@@ -102,14 +115,14 @@ export function Search({
}
)
const [previousResults, setPreviousResults] = useState<SearchResult[] | undefined>()
const [previousData, setPreviousData] = useState<Data | undefined>()
useEffect(() => {
if (results) {
setPreviousResults(results)
if (data) {
setPreviousData(data)
} else if (!query) {
setPreviousResults(undefined)
setPreviousData(undefined)
}
}, [results, query])
}, [data, query])
// The `isLoading` boolean will become false every time the useSWR hook
// fires off a new XHR. So it toggles from false/true often.
@@ -124,7 +137,7 @@ export function Search({
// mean saying "Loading..." is a lie!
// That's why we combine them into a final one. We're basically doing
// this to favor *NOT* saying "Loading...".
const isLoadingRaw = Boolean(query && !results && !searchError)
const isLoadingRaw = Boolean(query && !data && !searchError)
const [isLoadingDebounced] = useDebounce<boolean>(isLoadingRaw, 500)
const isLoading = isLoadingRaw && isLoadingDebounced
@@ -216,7 +229,7 @@ export function Search({
isHeaderSearch={isHeaderSearch}
isMobileSearch={isMobileSearch}
isLoading={isLoading}
results={previousResults}
results={previousData}
closeSearch={closeSearch}
debug={debug}
query={query}
@@ -342,7 +355,7 @@ function ShowSearchResults({
isHeaderSearch: boolean
isMobileSearch: boolean
isLoading: boolean
results: SearchResult[] | undefined
results: Data | undefined
closeSearch: () => void
debug: boolean
query: string
@@ -461,11 +474,33 @@ function ShowSearchResults({
{t('search_results_for')}: {query}
</h1>
<p className="ml-4 mb-4 text-normal f5">
{t('matches_displayed')}: {results.length === 0 ? t('no_results') : results.length}
{results.meta.found.value === 0 ? (
t('no_results')
) : (
<span>
{t('matches_found')}: {results.meta.found.value.toLocaleString()}
</span>
)}
.{' '}
{results.meta.found.value > results.hits.length && (
<span>
{t('matches_displayed')}:{' '}
{results.meta.found.value === 0 ? t('no_results') : results.hits.length}
</span>
)}
</p>
<ActionList variant="full">
{results.map(({ url, breadcrumbs, title, content, score, popularity }, index) => {
{results.hits.map((hit, index) => {
const { url, breadcrumbs, title, highlights, score, popularity } = hit
const contentHTML =
highlights.content && highlights.content.length > 0
? highlights.content.join('<br>')
: ''
const titleHTML =
highlights.title && highlights.title.length > 0 ? highlights.title[0] : title
return (
<ActionList.Item className="width-full" key={url}>
<Link
@@ -476,8 +511,8 @@ function ShowSearchResults({
type: EventType.searchResult,
search_result_query: Array.isArray(query) ? query[0] : query,
search_result_index: index,
search_result_total: results.length,
search_result_rank: (results.length - index) / results.length,
search_result_total: results.hits.length,
search_result_rank: (results.hits.length - index) / results.hits.length,
search_result_url: url,
})
}}
@@ -487,17 +522,10 @@ function ShowSearchResults({
className={cx('list-style-none', styles.resultsContainer)}
>
<div className={cx('py-2 px-3')}>
{/* Breadcrumbs in search records don't include the page title. These fields may contain <mark> elements that we need to render */}
<Label size="small" variant="accent">
{breadcrumbs.length === 0
? title.replace(/<\/?[^>]+(>|$)|(\/)/g, '')
: breadcrumbs
.split(' / ')
.slice(0, 1)
.join(' ')
.replace(/<\/?[^>]+(>|$)|(\/)/g, '')}
{breadcrumbs ? breadcrumbs.split(' / ')[0] : title}
</Label>
{debug && (
{debug && score !== undefined && popularity !== undefined && (
<small className="float-right">
score: {score.toFixed(4)} popularity: {popularity.toFixed(4)}
</small>
@@ -505,13 +533,13 @@ function ShowSearchResults({
<h2
className={cx('mt-2 text-normal f3 d-block')}
dangerouslySetInnerHTML={{
__html: title,
__html: titleHTML,
}}
/>
<div
className={cx(styles.searchResultContent, 'mt-1 d-block overflow-hidden')}
style={{ maxHeight: '2.5rem' }}
dangerouslySetInnerHTML={{ __html: content }}
dangerouslySetInnerHTML={{ __html: contentHTML }}
/>
<div
className={'d-block mt-2 opacity-70 text-small'}

View File

@@ -66,24 +66,35 @@ The custom label is created and assigned to the self-hosted runner. Custom label
1. Click on the assigned label to remove it from your self-hosted runner. {% data reusables.actions.actions-unused-labels %}
{% endif %}
## Using the configuration script to create and assign labels
## Programmatically assign labels
You can use the configuration script on the self-hosted runner to create and assign custom labels. For example, this command assigns a label named `gpu` to the self-hosted runner.
You can programmatically assign labels to a self-hosted runner after the runner is created, or during its initial configuration.
```shell
./config.sh --labels gpu
```
* To programmatically assign labels to an existing self-hosted runner, you must use the REST API. For more information, see the "[Self-hosted runners](/rest/actions/self-hosted-runners)" REST API.
* To programmatically assign labels to a self-hosted runner during the initial runner configuration, you can pass label names to the `config` script using the `labels` parameter.
The label is created if it does not already exist. You can also use this approach to assign the default labels to runners, such as `x64` or `linux`. When default labels are assigned using the configuration script, {% data variables.product.prodname_actions %} accepts them as given and does not validate that the runner is actually using that operating system or architecture.
{% note %}
**Note:** You cannot use the `config` script to assign labels to an existing self-hosted runner.
{% endnote %}
You can use comma separation to assign multiple labels. For example:
For example, this command assigns a label named `gpu` when configuring a new self-hosted runner:
```shell
./config.sh --labels gpu,x64,linux
```
```
./config.sh --url <REPOSITORY_URL> --token <REGISTRATION_TOKEN> --labels gpu
```
{% note %}
The label is created if it does not already exist. You can also use this approach to assign the default labels to runners, such as `x64` or `linux`. When default labels are assigned using the configuration script, {% data variables.product.prodname_actions %} accepts them as given and does not validate that the runner is actually using that operating system or architecture.
** Note:** If you replace an existing runner, then you must reassign any custom labels.
You can use comma separation to assign multiple labels. For example:
{% endnote %}
```
./config.sh --url <REPOSITORY_URL> --token <REGISTRATION_TOKEN> --labels gpu,x64,linux
```
{% note %}
** Note:** If you replace an existing runner, then you must reassign any custom labels.
{% endnote %}

View File

@@ -21,7 +21,7 @@ topics:
Enterprise owners on {% data variables.product.product_name %} can control the requirements for authentication and access to the enterprise's resources.
You can choose to allow members to create and manage user accounts, or your enterprise can create and manage accounts for members with {% data variables.product.prodname_emus %}. If you allow members to manage their own accounts, you can also configure SAML authentication to both increase security and centralize identity and access for the web applications that your team uses.
{% data reusables.enterprise.ghec-authentication-options %}
After learning more about these options, to determine which method is best for your enterprise, see "[Identifying the best authentication method for your enterprise](#identifying-the-best-authentication-method-for-your-enterprise)."

View File

@@ -0,0 +1,49 @@
---
title: Best practices for enterprises
shortTitle: Best practices
intro: Learn {% data variables.product.company_short %}-recommended practices for your enterprise.
versions:
ghec: '*'
ghes: '*'
ghae: '*'
type: overview
topics:
- Accounts
- Enterprise
- Fundamentals
---
{% ifversion ghec %}
## Identify the best authentication method for your enterprise
{% data reusables.enterprise.ghec-authentication-options %}
For help identifying the authentication method that will best meet your needs, see "[About authentication for your enterprise](/admin/identity-and-access-management/managing-iam-for-your-enterprise/about-authentication-for-your-enterprise)."
{% endif %}
## Use policies
We recommend using policies to enforce business rules and regulatory compliance.
{% data reusables.enterprise.about-policies %} For more information, see "[About enterprise policies](/admin/policies/enforcing-policies-for-your-enterprise/about-enterprise-policies)."
## Minimize the number of organizations
Large enterprises often need multiple organizations, but try to create as few as possible to reflect top-level corporate divisions. A smaller number of organizations encourages innersource practices and allows discussions to involve a wider audience.
Instead, you can manage repository access and security requirements at a more granular level within each organization by using teams. For more information, see "[About teams](/organizations/organizing-members-into-teams/about-teams)."
## Avoid extensive collaboration in user-owned repositories
We recommend collaborating in organization-owned repositories whenever possible and minimizing collaboration in user-owned repositories. Organization-owned repositories have more sophisticated security and administrative features, and they remain accessible even as enterprise membership changes.
## Use human-readable usernames
{% ifversion ghec %}If you control the usernames for enterprise members, use{% else %}Use{% endif %} human-readable usernames, and avoid machine-generated IDs that are difficult for humans to read.
You can manage the display of usernames within your enterprise's private repositories. For more information, see "[Managing the display of member names in your organization](/organizations/managing-organization-settings/managing-the-display-of-member-names-in-your-organization)."
## Further reading
- "[Best practices for repositories](/repositories/creating-and-managing-repositories/best-practices-for-repositories)"
- "[Best practices for organizations](/organizations/collaborating-with-groups-in-organizations/best-practices-for-organizations)"

View File

@@ -16,6 +16,7 @@ children:
- /about-enterprise-accounts
- /system-overview
- /about-the-github-enterprise-api
- /best-practices-for-enterprises
- /creating-an-enterprise-account
- /accessing-compliance-reports-for-your-enterprise
---

View File

@@ -54,9 +54,12 @@ If you downgrade your organization from {% data variables.product.prodname_team
If you downgrade your organization from {% data variables.product.prodname_ghe_cloud %} to {% data variables.product.prodname_team %} or {% data variables.product.prodname_free_team %}, the account will lose access to advanced security, compliance, and deployment controls. {% data reusables.gated-features.more-info %}
{% note %}
**Note:** If you're currently trialing {% data variables.product.prodname_ghe_cloud %}, and you don't purchase {% data variables.product.prodname_enterprise %} before the trial ends, your organization will be automatically downgraded to {% data variables.product.prodname_free_team %} or {% data variables.product.prodname_team %}. For more information, see "[Setting up a trial of {% data variables.product.prodname_ghe_cloud %}](/get-started/signing-up-for-github/setting-up-a-trial-of-github-enterprise-cloud#finishing-your-trial)."
**Notes:**
- If your organization is owned by an enterprise account, billing cannot be managed at the organization level. To downgrade, you must remove the organization from the enterprise account first. For more information, see "[Removing organizations from your enterprise](/enterprise-cloud@latest/admin/user-management/managing-organizations-in-your-enterprise/removing-organizations-from-your-enterprise)."
- If you're currently trialing {% data variables.product.prodname_ghe_cloud %}, and you don't purchase {% data variables.product.prodname_enterprise %} before the trial ends, your organization will be automatically downgraded to {% data variables.product.prodname_free_team %} or {% data variables.product.prodname_team %}. For more information, see "[Setting up a trial of {% data variables.product.prodname_ghe_cloud %}](/get-started/signing-up-for-github/setting-up-a-trial-of-github-enterprise-cloud#finishing-your-trial)."
{% endnote %}

View File

@@ -28,7 +28,7 @@ shortTitle: About the security overview
## About the security overview
The security overview provides high-level summaries of the security status of an organization or enterprise and make it easy to identify problematic repositories that require intervention. You can also use the security overview to see which repositories have enabled specific security features and to configure any available security features that are not currently in use. {% ifversion fpt %}For more information, see [the {% data variables.product.prodname_ghe_cloud %} documentation](/enterprise-cloud@latest/code-security/security-overview/about-the-security-overview).{% endif %}
{% data reusables.security-overview.about-the-security-overview %} {% ifversion fpt %}For more information, see [the {% data variables.product.prodname_ghe_cloud %} documentation](/enterprise-cloud@latest/code-security/security-overview/about-the-security-overview).{% endif %}
{% ifversion ghec or ghes or ghae %}
The security overview shows which security features are enabled for repositories and consolidate alerts for each feature.

View File

@@ -46,8 +46,6 @@ The dependency review feature becomes available when you enable the dependency g
{% ifversion fpt or ghec or ghes > 3.5 or ghae > 3.5 %}
## Dependency review enforcement
{% data reusables.dependency-review.dependency-review-action-beta-note %}
The action is available for all {% ifversion fpt or ghec %}public repositories, as well as private {% endif %}repositories that have {% data variables.product.prodname_GH_advanced_security %} enabled.
{% data reusables.dependency-review.action-enterprise %}

View File

@@ -1,6 +1,7 @@
---
title: Configuring dependency review
intro: You can use dependency review to catch vulnerabilities before they are added to your project.
miniTocMaxHeadingLevel: 3
shortTitle: Configure dependency review
versions:
fpt: '*'
@@ -50,9 +51,8 @@ Dependency review is available when dependency graph is enabled for {% data vari
{% endif %}
{% ifversion dependency-review-action-configuration %}
## Configuring the {% data variables.product.prodname_dependency_review_action %}
## About configuring the {% data variables.product.prodname_dependency_review_action %}
{% data reusables.dependency-review.dependency-review-action-beta-note %}
{% data reusables.dependency-review.dependency-review-action-overview %}
The following configuration options are available.
@@ -63,7 +63,11 @@ The following configuration options are available.
{%- ifversion dependency-review-action-licenses %}
| `allow-licenses` | Optional | Contains a list of allowed licenses. You can find the possible values for this parameter in the [Licenses](/rest/licenses) page of the API documentation.</br>The action will fail on pull requests that introduce dependencies with licenses that do not match the list.|{% endif %}
{%- ifversion dependency-review-action-licenses %}
| `deny-licenses` | Optional | Contains a list of prohibited licenses. You can find the possible values for this parameter in the [Licenses](/rest/licenses) page of the API documentation.</br>The action will fail on pull requests that introduce dependencies with licenses that match the list.|{% endif %}
| `deny-licenses` | Optional | Contains a list of prohibited licenses. You can find the possible values for this parameter in the [Licenses](/rest/licenses) page of the API documentation.</br>The action will fail on pull requests that introduce dependencies with licenses that match the list.|{% endif %}{% ifversion dependency-review-action-fail-on-scopes %}
| `fail-on-scopes` | Optional | Contains a list of strings representing the build environments you want to support (`development`, `runtime`, `unknown`). </br>The action will fail on pull requests that introduce vulnerabilites in the scopes that match the list.|{% endif %}
| `allow-ghsas` | Optional | Contains a list of {% data variables.product.prodname_advisory_database %} IDs that can be skipped during detection. You can find the possible values for this parameter in the [{% data variables.product.prodname_advisory_database %}](https://github.com/advisories). |
| `config-file` | Optional | Specifies a path to a configuration file. The configuration file can be local to the repository or a file located in an external repository.|
| `external-repo-token` | Optional | Specifies a token for fetching the configuration file, if the file resides in a private external repository. The token must have read access to the repository.|
{% ifversion dependency-review-action-licenses %}
{% tip %}
@@ -73,37 +77,139 @@ The following configuration options are available.
{% endtip %}
{% endif %}
This {% data variables.product.prodname_dependency_review_action %} example file illustrates how you can use these configuration options. Notice that the example uses short version number for the action (`v2`) instead of a semver release number (for example, `v2.0.8`). This ensures that you use the most recent minor version of the action.
## Configuring the {% data variables.product.prodname_dependency_review_action %}
```yaml{:copy}
name: 'Dependency Review'
on: [pull_request]
There are two methods of configuring the {% data variables.product.prodname_dependency_review_action %}:
- Inlining the configuration options in your workflow file.
- Referencing a configuration file in your workflow file.
permissions:
contents: read
Notice that all of the examples use a short version number for the action (`v3`) instead of a semver release number (for example, `v3.0.8`). This ensures that you use the most recent minor version of the action.
### Using inline configuration to set up the {% data variables.product.prodname_dependency_review_action %}
jobs:
dependency-review:
runs-on: ubuntu-latest
steps:
- name: 'Checkout Repository'
uses: {% data reusables.actions.action-checkout %}
- name: Dependency Review
uses: actions/dependency-review-action@v2
with:
# Possible values: "critical", "high", "moderate", "low"
fail-on-severity: critical
{% ifversion dependency-review-action-licenses %}
# You can only can only include one of these two options: `allow-licenses` and `deny-licences`
# ([String]). Only allow these licenses (optional)
# Possible values: Any `spdx_id` value(s) from https://docs.github.com/en/rest/licenses
# allow-licenses: GPL-3.0, BSD-3-Clause, MIT
1. Add a new YAML workflow to your `.github/workflows` folder.
{% ifversion ghes %}For `runs-on`, the default label is `self-hosted`. You can replace the default label with the label of any of your runners.{% endif %}
```yaml{:copy}
name: 'Dependency Review'
on: [pull_request]
# ([String]). Block the pull request on these licenses (optional)
# Possible values: Any `spdx_id` value(s) from https://docs.github.com/en/rest/licenses
# deny-licenses: LGPL-2.0, BSD-2-Clause
{% endif %}
```
permissions:
contents: read
jobs:
dependency-review:
{% ifversion ghes %}runs-on: self-hosted
{% else %}runs-on: ubuntu-latest
{% endif %}steps:
- name: 'Checkout Repository'
uses: {% data reusables.actions.action-checkout %}
- name: Dependency Review
uses: actions/dependency-review-action@v3
```
1. Specify your settings.
This {% data variables.product.prodname_dependency_review_action %} example file illustrates how you can use the available configuration options.
```yaml{:copy}
name: 'Dependency Review'
on: [pull_request]
permissions:
contents: read
jobs:
dependency-review:
{% ifversion ghes %}runs-on: self-hosted
{% else %}runs-on: ubuntu-latest
{% endif %}steps:
- name: 'Checkout Repository'
uses: {% data reusables.actions.action-checkout %}
- name: Dependency Review
uses: actions/dependency-review-action@v3
with:
# Possible values: "critical", "high", "moderate", "low"
fail-on-severity: critical
{% ifversion dependency-review-action-licenses %}
# You can only include one of these two options: `allow-licenses` and `deny-licences`
# ([String]). Only allow these licenses (optional)
# Possible values: Any `spdx_id` value(s) from https://docs.github.com/en/rest/licenses
allow-licenses: GPL-3.0, BSD-3-Clause, MIT
# ([String]). Block the pull request on these licenses (optional)
# Possible values: Any `spdx_id` value(s) from https://docs.github.com/en/rest/licenses
deny-licenses: LGPL-2.0, BSD-2-Clause
{% endif %}
# ([String]). Skip these {% data variables.product.prodname_advisory_database %} IDs during detection (optional)
# Possible values: Any valid {% data variables.product.prodname_advisory_database %} ID from https://github.com/advisories
allow-ghsas: GHSA-abcd-1234-5679, GHSA-efgh-1234-5679
{% ifversion dependency-review-action-fail-on-scopes %}
# ([String]). Block pull requests that introduce vulnerabilities in the scopes that match this list (optional)
# Possible values: "development", "runtime", "unknown"
fail-on-scopes: development, runtime
{% endif %}
```
### Using a configuration file to set up {% data variables.product.prodname_dependency_review_action %}
1. Add a new YAML workflow to your `.github/workflows` folder and use `config-file` to specify that you are using a configuration file.
{% ifversion ghes %}For `runs-on`, the default label is `self-hosted`. You can replace the default label with the label of any of your runners.{% endif %}
```yaml{:copy}
name: 'Dependency Review'
on: [pull_request]
permissions:
contents: read
jobs:
dependency-review:
{% ifversion ghes %}runs-on: self-hosted
{% else %}runs-on: ubuntu-latest
{% endif %}steps:
- name: 'Checkout Repository'
uses: {% data reusables.actions.action-checkout %}
- name: Dependency Review
uses: actions/dependency-review-action@v3
with:
# ([String]). Representing a path to a configuration file local to the repository or in an external repository.
# Possible values: An absolute path to a local file or an external file.
config-file: './.github/dependency-review-config.yml'
# Syntax for an external file: OWNER/REPOSITORY/FILENAME@BRANCH
config-file: 'github/octorepo/dependency-review-config.yml@main'
# ([Token]) Use if your configuration file resides in a private external repository.
# Possible values: Any GitHub token with read access to the private external repository.
external-repo-token: 'ghp_123456789abcde'
```
1. Create the configuration file in the path you have specified.
This YAML example file illustrates how you can use the available configuration options.
```yaml{:copy}
# Possible values: "critical", "high", "moderate", "low"
fail-on-severity: critical
{% ifversion dependency-review-action-licenses %}
# You can only include one of these two options: `allow-licenses` and `deny-licences`
# ([String]). Only allow these licenses (optional)
# Possible values: Any `spdx_id` value(s) from https://docs.github.com/en/rest/licenses
allow-licenses:
- GPL-3.0
- BSD-3-Clause
- MIT
# ([String]). Block the pull request on these licenses (optional)
# Possible values: Any `spdx_id` value(s) from https://docs.github.com/en/rest/licenses
deny-licenses:
- LGPL-2.0
- BSD-2-Clause
{% endif %}
# ([String]). Skip these {% data variables.product.prodname_advisory_database %} IDs during detection (optional)
# Possible values: Any valid {% data variables.product.prodname_advisory_database %} ID from https://github.com/advisories
allow-ghsas:
- GHSA-abcd-1234-5679
- GHSA-efgh-1234-5679
{% ifversion dependency-review-action-fail-on-scopes %}
# ([String]). Block pull requests that introduce vulnerabilities in the scopes that match this list (optional)
# Possible values: "development", "runtime", "unknown"
fail-on-scopes:
- development
- runtime
{% endif %}
```
For further details about the configuration options, see [`dependency-review-action`](https://github.com/actions/dependency-review-action#readme).
{% endif %}

View File

@@ -42,7 +42,7 @@ You can work with {% data variables.product.prodname_github_codespaces %} in the
If you have not already done so, run `gh auth login` to authenticate with your {% data variables.product.prodname_dotcom %} account.
To use `gh` to work with {% data variables.product.prodname_github_codespaces %}, type `gh codespace <COMMAND>` or its alias `gh cs <COMMAND>`.
To use `gh` to work with {% data variables.product.prodname_github_codespaces %}, type `gh codespace SUBCOMMAND` or its alias `gh cs SUBCOMMAND`.
As an example of a series of commands you might use to work with {% data variables.product.prodname_github_codespaces %}, you could:
@@ -51,15 +51,15 @@ As an example of a series of commands you might use to work with {% data variabl
* Create a new codespace for the required repository branch:<br>
`gh codespace create -r github/docs -b main`
* SSH into the new codespace:<br>
`gh codespace ssh -c mona-github-docs-v4qxrv7rfwv9w`
`gh codespace ssh -c octocat-literate-space-parakeet-7gwrqp9q9jcx4vq`
* Forward a port to your local machine:<br>
`gh codespace ports forward 8000:8000 -c mona-github-docs-v4qxrv7rfwv9w`
`gh codespace ports forward 8000:8000 -c octocat-literate-space-parakeet-7gwrqp9q9jcx4vq`
## `gh` commands for {% data variables.product.prodname_github_codespaces %}
The sections below give example commands for each of the available operations.
For a complete reference of `gh` commands for {% data variables.product.prodname_github_codespaces %}, including details of all available options for each command, see the {% data variables.product.prodname_cli %} online help for "[gh codespace](https://cli.github.com/manual/gh_codespace)." Alternatively, use `gh codespace [<SUBCOMMAND>...] --help` on the command line.
For a complete reference of `gh` commands for {% data variables.product.prodname_github_codespaces %}, including details of all available options for each command, see the {% data variables.product.prodname_cli %} online help for "[gh codespace](https://cli.github.com/manual/gh_codespace)." Alternatively, on the command line, use `gh codespace --help` for general help or `gh codespace SUBCOMMAND --help` for help with a specific subcommand.
{% note %}
@@ -163,7 +163,7 @@ The location of files and directories on the codespace is relative to the home d
* Copy a file to the directory in which a repository is checked out in a codespace:
`gh codespace cp myfile.txt remote:/workspaces/<REPOSITORY-NAME>`
`gh codespace cp myfile.txt remote:/workspaces/REPOSITORY-NAME`
* Copy a file from a codespace to the current directory on the local machine:
@@ -233,7 +233,7 @@ You can use the {% data variables.product.prodname_cli %} extension to create a
### Change the machine type of a codespace
```shell
gh codespace edit -m <em>machine-type-name</em>
gh codespace edit -m MACHINE-TYPE-NAME
```
For more information, see the "{% data variables.product.prodname_cli %}" tab of "[Changing the machine type for your codespace](/codespaces/customizing-your-codespace/changing-the-machine-type-for-your-codespace)."

View File

@@ -205,6 +205,8 @@ Members of your organization or enterprise can use GitHub's learning and support
### 1. Reading about {% data variables.product.prodname_ghe_cloud %} on {% data variables.product.prodname_docs %}
You can read documentation that reflects the features available with {% data variables.product.prodname_ghe_cloud %}. For more information, see "[About versions of {% data variables.product.prodname_docs %}](/get-started/learning-about-github/about-versions-of-github-docs)."
{% data reusables.enterprise.best-practices %}
### 2. Learning with {% data variables.product.prodname_learning %}
Members of your organization or enterprise can learn new skills by completing fun, realistic projects in your very own GitHub repository with [{% data variables.product.prodname_learning %}](https://skills.github.com/). Each course is a hands-on lesson created by the GitHub community and taught by a friendly bot.

View File

@@ -119,6 +119,8 @@ Your enterprise members can learn more about Git and {% data variables.product.p
You can read documentation that reflects the features available with {% data variables.product.prodname_ghe_server %}. For more information, see "[About versions of {% data variables.product.prodname_docs %}](/get-started/learning-about-github/about-versions-of-github-docs)."
{% data reusables.enterprise.best-practices %}
### 2. Learning with {% data variables.product.prodname_learning %}
{% data reusables.getting-started.learning-enterprise %}

View File

@@ -62,6 +62,8 @@ After you set up your trial, you can explore {% data variables.product.prodname_
{% data reusables.docs.you-can-read-docs-for-your-product %}
{% data reusables.enterprise.best-practices %}
{% data reusables.products.product-roadmap %}
## Finishing your trial

View File

@@ -55,6 +55,8 @@ To get the most out of your trial, follow these steps:
{% endnote %}
{% data reusables.enterprise.best-practices %}
{% data reusables.products.product-roadmap %}
## Finishing your trial

View File

@@ -28,7 +28,7 @@ You can configure the organization to meet the unique needs of your group by man
To harden your organization's security, you can enforce security requirements and review the organization's audit log. For more information, see "[Keeping your organization secure](/organizations/keeping-your-organization-secure)."
{% data reusables.organizations.org-ownership-recommendation %} For more information, see "[Maintaining ownership continuity for your organization](/organizations/managing-peoples-access-to-your-organization-with-roles/maintaining-ownership-continuity-for-your-organization)."
To learn how to use organizations most effectively, see "[Best practices for organizations](/organizations/collaborating-with-groups-in-organizations/best-practices-for-organizations)."
{% ifversion fpt or ghec %}
## About feature availability

View File

@@ -0,0 +1,35 @@
---
title: Best practices for organizations
shortTitle: Best practices
intro: Learn {% data variables.product.prodname_dotcom %}-recommended practices for your organization.
versions:
fpt: '*'
ghes: '*'
ghae: '*'
ghec: '*'
topics:
- Organizations
- Teams
---
## Assign multiple owners
{% data reusables.organizations.org-ownership-recommendation %} For more information, see "[Maintaining ownership continuity for your organization](/organizations/managing-peoples-access-to-your-organization-with-roles/maintaining-ownership-continuity-for-your-organization)."
## Use teams
We recommend using teams to facilitate collaboration in your organization. For more information, see "[About teams](/organizations/organizing-members-into-teams/about-teams)."
{% ifversion ghec %}
We highly recommend managing team membership through your identity provider (IdP). For more information, see "[Managing team synchronization for your organization](/organizations/managing-saml-single-sign-on-for-your-organization/managing-team-synchronization-for-your-organization)."
{% data reusables.enterprise-accounts.emu-scim-note %}
{% endif %}
We recommend keeping teams visible whenever possible and reserving secret teams for sensitive situations. For more information, see "[Changing team visibility](/organizations/organizing-members-into-teams/changing-team-visibility)."
{% ifversion ghec or ghes or ghae %}
## Use the security overview
{% data reusables.security-overview.about-the-security-overview %} For more information, see "[About the security overview](/code-security/security-overview/about-the-security-overview)."
{% endif %}

View File

@@ -16,6 +16,7 @@ topics:
children:
- /about-organizations
- /about-your-organization-dashboard
- /best-practices-for-organizations
- /creating-a-new-organization-from-scratch
- /accessing-your-organizations-settings
- /customizing-your-organizations-profile

View File

@@ -42,6 +42,8 @@ You can use repositories to manage your work and collaborate with others.
{% data reusables.repositories.repo-size-limit %}
To learn how to use repositories most effectively, see "[Best practices for repositories](/repositories/creating-and-managing-repositories/best-practices-for-repositories)."
## About repository visibility
You can restrict who has access to a repository by choosing a repository's visibility: {% ifversion ghes or ghec %}public, internal, or private{% elsif ghae %}private or internal{% else %} public or private{% endif %}.
@@ -138,7 +140,6 @@ The compare view and pull requests pages display a list of commits between the `
## Further reading
- "[Creating a new repository](/articles/creating-a-new-repository)"
- "[About forks](/github/collaborating-with-pull-requests/working-with-forks/about-forks)"
- "[Collaborating with issues and pull requests](/categories/collaborating-with-issues-and-pull-requests)"
- "[Managing your work on {% data variables.product.prodname_dotcom %}](/categories/managing-your-work-on-github/)"

View File

@@ -0,0 +1,30 @@
---
title: Best practices for repositories
shortTitle: Best practices
intro: Learn how to use repositories most effectively.
versions:
fpt: '*'
ghes: '*'
ghae: '*'
ghec: '*'
topics:
- Repositories
---
## Create a README file
To make it easier for people to understand and navigate your work, we recommend that you create a README file for every repository.
{% data reusables.repositories.about-READMEs %} For more information, see "[About READMEs](/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-readmes)."
## Favor branching over forking
To streamline collaboration, we recommend that regular collaborators work from a single repository, creating pull requests between branches instead of between repositories. Forking is best suited for accepting contributions from people that are unaffiliated with a project, such as open-source contributors.
To maintain quality of important branches, such as `main`, while using a branching workflow, you can use protected branches with required status checks and pull request reviews. For more information, see "[About protected branches](/repositories/configuring-branches-and-merges-in-your-repository/defining-the-mergeability-of-pull-requests/about-protected-branches)."
## Use {% data variables.large_files.product_name_long %}
To optimize performance, {% data variables.location.product_location %} limits the sizes of files allowed in repositories. For more information, see "[About large files on {% data variables.product.prodname_dotcom %}](/repositories/working-with-files/managing-large-files/about-large-files-on-github)."
To track large files in a Git repository, we recommend using {% data variables.large_files.product_name_long %} ({% data variables.large_files.product_name_short %}). For more information, see "[About {% data variables.large_files.product_name_long %}](/repositories/working-with-files/managing-large-files/about-git-large-file-storage)."

View File

@@ -13,6 +13,7 @@ topics:
- Repositories
children:
- /about-repositories
- /best-practices-for-repositories
- /creating-a-new-repository
- /creating-a-repository-from-a-template
- /creating-a-template-repository

View File

@@ -17,7 +17,7 @@ topics:
---
## About READMEs
You can add a README file to a repository to communicate important information about your project. A README, along with a repository license, citation file{% ifversion fpt or ghec %}, contribution guidelines, and a code of conduct{% elsif ghes %} and contribution guidelines{% endif %}, communicates expectations for your project and helps you manage contributions.
{% data reusables.repositories.about-READMEs %}
For more information about providing guidelines for your project, see {% ifversion fpt or ghec %}"[Adding a code of conduct to your project](/communities/setting-up-your-project-for-healthy-contributions/adding-a-code-of-conduct-to-your-project)" and {% endif %}"[Setting up your project for healthy contributions](/communities/setting-up-your-project-for-healthy-contributions)."

View File

@@ -14,6 +14,4 @@ allowTitleToDifferFromFilename: true
## About the Dependency review API
{% data reusables.dependency-review.dependency-review-api-beta-note %}
The Dependency Review API allows you to understand dependency changes, and the security impact of these changes, before you add them to your environment. You can view the diff of dependencies between two commits of a repository, including vulnerability data for any version updates with known vulnerabilities. For more information about dependency review, see "[About dependency review](/code-security/supply-chain-security/understanding-your-software-supply-chain/about-dependency-review)."

View File

@@ -61,7 +61,8 @@ We do not allow content or activity on GitHub that is:
* creation of or participation in secondary markets for the purpose of the proliferation of inauthentic activity;
* using GitHub as a platform for propagating abuse on other platforms;
* phishing or attempted phishing; or
* using our servers for any form of excessive automated bulk activity, to place undue burden on our servers through automated means, or to relay any form of unsolicited advertising or solicitation through our servers, such as get-rich-quick schemes.
* using our servers for any form of excessive automated bulk activity, to place undue burden on our servers through automated means, or to relay any form of unsolicited advertising or solicitation through our servers, such as get-rich-quick schemes;
* incentivized by (or incentivizes inauthentic engagement with) rewards such as cryptocurrency airdrops, tokens, credits, gifts or other give-aways.
## 5. Site Access and Safety
We do not allow content or activity on GitHub that:

View File

@@ -0,0 +1,7 @@
# Reference: Issue #7739 - Dependency review action, new config option "fail-on-scopes" added.
versions:
fpt: '*'
ghec: '*'
ghes: '> 3.8'
ghae: '> 3.8'

View File

@@ -1,5 +0,0 @@
{% note %}
**Note**: The Dependency Review API is currently in public beta and subject to change.
{% endnote %}

View File

@@ -0,0 +1 @@
To learn how your enterprise can use {% data variables.product.product_name %} most effectively, see "[Best practices for enterprises](/admin/overview/best-practices-for-enterprises)."

View File

@@ -0,0 +1 @@
You can choose to allow members to create and manage user accounts, or your enterprise can create and manage accounts for members with {% data variables.product.prodname_emus %}. If you allow members to manage their own accounts, you can also configure SAML authentication to both increase security and centralize identity and access for the web applications that your team uses.

View File

@@ -1 +1 @@
Projects maintained and managed by one sole organization owner can easily become inaccessible if the organization owner is unreachable. We recommend an organization have at least two people with *owner* permissions to ensure no one will lose access to a project.
If an organization only has one owner, the organization's projects can become inaccessible if the owner is unreachable. To ensure that no one will lose access to a project, we recommend that at least two people within each organization have the owner role.

View File

@@ -0,0 +1 @@
You can add a README file to a repository to communicate important information about your project. A README, along with a repository license, citation file{% ifversion fpt or ghec %}, contribution guidelines, and a code of conduct{% elsif ghes %} and contribution guidelines{% endif %}, communicates expectations for your project and helps you manage contributions.

View File

@@ -0,0 +1 @@
The security overview provides high-level summaries of the security status of an organization or enterprise and make it easy to identify problematic repositories that require intervention. You can also use the security overview to see which repositories have enabled specific security features and to configure any available security features that are not currently in use.

View File

@@ -32,6 +32,7 @@ search:
no_results: No results found
search_results_for: Search results for
no_content: No content
matches_found: Results found
matches_displayed: Matches displayed
search_error: An error occurred trying to perform the search.
description: Enter a search term to find it in the GitHub Documentation.

View File

@@ -1,5 +1,8 @@
import { Client } from '@elastic/elasticsearch'
export const POSSIBLE_HIGHLIGHT_FIELDS = ['title', 'content', 'headings']
export const DEFAULT_HIGHLIGHT_FIELDS = ['title', 'content']
const ELASTICSEARCH_URL = process.env.ELASTICSEARCH_URL
const isDevMode = process.env.NODE_ENV !== 'production'
@@ -37,6 +40,7 @@ export async function getSearchResults({
topics,
includeTopics,
usePrefixSearch,
highlights,
}) {
if (topics && !Array.isArray(topics)) {
throw new Error("'topics' has to be an array")
@@ -72,7 +76,8 @@ export async function getSearchResults({
matchQuery.bool.filter = topicsFilter
}
const highlight = getHighlightConfiguration(query)
const highlightFields = highlights || DEFAULT_HIGHLIGHT_FIELDS
const highlight = getHighlightConfiguration(query, highlightFields)
const searchQuery = {
highlight,
@@ -141,7 +146,7 @@ export async function getSearchResults({
// const hitsAll = result.hits // ES >7.11
const hitsAll = result.body // ES <=7.11
const hits = getHits(hitsAll.hits.hits, { indexName, debug, includeTopics })
const hits = getHits(hitsAll.hits.hits, { indexName, debug, includeTopics, highlightFields })
const t1 = new Date()
const meta = {
@@ -345,14 +350,26 @@ function getMatchQueries(query, { usePrefixSearch, fuzzy }) {
return matchQueries
}
function getHits(hits, { indexName, debug, includeTopics }) {
function getHits(hits, { indexName, debug, includeTopics, highlightFields }) {
return hits.map((hit) => {
// Return `hit.highlights[...]` based on the highlight fields requested.
// So if you searched with `&highlights=headings&highlights=content`
// this will become:
// {
// content: [...],
// headings: [...]
// }
// even if there was a match on 'title'.
const hitHighlights = Object.fromEntries(
highlightFields.map((key) => [key, (hit.highlight && hit.highlight[key]) || []])
)
const result = {
id: hit._id,
url: hit._source.url,
title: hit._source.title,
breadcrumbs: hit._source.breadcrumbs,
highlights: hit.highlight || {},
highlights: hitHighlights,
}
if (includeTopics) {
result.topics = hit._source.topics || []
@@ -374,31 +391,38 @@ function getHits(hits, { indexName, debug, includeTopics }) {
// of highlights of content under each title. If we feel it shows too
// many highlights in the search result UI, we can come back here
// and change it to something more appropriate.
function getHighlightConfiguration(query) {
return {
pre_tags: ['<mark>'],
post_tags: ['</mark>'],
fields: {
title: {
fragment_size: 200,
number_of_fragments: 1,
},
headings: { fragment_size: 150, number_of_fragments: 2 },
// The 'no_match_size' is so we can display *something* for the
// preview if there was no highlight match at all within the content.
content: {
fragment_size: 150,
number_of_fragments: 3,
no_match_size: 150,
function getHighlightConfiguration(query, highlights) {
const fields = {}
if (highlights.includes('title')) {
fields.title = {
fragment_size: 200,
number_of_fragments: 1,
}
}
if (highlights.includes('headings')) {
fields.headings = { fragment_size: 150, number_of_fragments: 2 }
}
if (highlights.includes('content')) {
// The 'no_match_size' is so we can display *something* for the
// preview if there was no highlight match at all within the content.
fields.content = {
fragment_size: 150,
number_of_fragments: 3,
no_match_size: 150,
highlight_query: {
match_phrase_prefix: {
content: {
query,
},
highlight_query: {
match_phrase_prefix: {
content: {
query,
},
},
},
},
}
}
return {
pre_tags: ['<mark>'],
post_tags: ['</mark>'],
fields,
}
}

View File

@@ -7,7 +7,11 @@ import { allVersions } from '../../lib/all-versions.js'
import statsd from '../../lib/statsd.js'
import { defaultCacheControl } from '../cache-control.js'
import catchMiddlewareError from '../catch-middleware-error.js'
import { getSearchResults } from './es-search.js'
import {
getSearchResults,
POSSIBLE_HIGHLIGHT_FIELDS,
DEFAULT_HIGHLIGHT_FIELDS,
} from './es-search.js'
// Used by the legacy search
const versions = new Set(Object.values(searchVersions))
@@ -182,7 +186,21 @@ const validationMiddleware = (req, res, next) => {
validate: (v) => v >= 1 && v <= 10,
},
{ key: 'sort', default_: DEFAULT_SORT, validate: (v) => POSSIBLE_SORTS.includes(v) },
{ key: 'debug', default_: Boolean(process.env.NODE_ENV === 'development' || req.query.debug) },
{
key: 'highlights',
default_: DEFAULT_HIGHLIGHT_FIELDS,
cast: (v) => (Array.isArray(v) ? v : [v]),
validate: (v) => {
for (const highlight of v) {
if (!POSSIBLE_HIGHLIGHT_FIELDS.includes(highlight)) {
throw new ValidationError(`highlight value '${highlight}' is not valid`)
}
}
return true
},
},
{ key: 'autocomplete', default_: false, cast: toBoolean },
{ key: 'debug', default_: process.env.NODE_ENV === 'development', cast: toBoolean },
]
const search = {}
@@ -221,11 +239,16 @@ const validationMiddleware = (req, res, next) => {
return next()
}
function toBoolean(value) {
if (value === 'true' || value === '1') return true
return false
}
router.get(
'/v1',
validationMiddleware,
catchMiddlewareError(async function search(req, res) {
const { indexName, query, page, size, debug, sort } = req.search
const { indexName, query, autocomplete, page, size, debug, sort, highlights } = req.search
// The getSearchResults() function is a mix of preparing the search,
// sending & receiving it, and post-processing the response from the
@@ -236,7 +259,16 @@ router.get(
const tags = ['version:v1', `indexName:${indexName}`]
const timed = statsd.asyncTimer(getSearchResults, 'api.search', tags)
const options = { indexName, query, page, size, debug, sort }
const options = {
indexName,
query,
page,
size,
debug,
sort,
highlights,
usePrefixSearch: autocomplete,
}
try {
const { meta, hits } = await timed(options)

View File

@@ -1,9 +1,13 @@
import { formatReleases, renderPatchNotes } from '../../lib/release-notes-utils.js'
import { allVersions } from '../../lib/all-versions.js'
export default async function ghaeReleaseNotesContext(req, res, next) {
if (!(req.pagePath.endsWith('/release-notes') || req.pagePath.endsWith('/admin'))) return next()
const requestedPlan = req.context.currentVersion.split('@')[0]
if (requestedPlan !== 'github-ae') return next()
if (
!allVersions[req.context.currentVersion] ||
req.context.currentVersion.split('@')[0] !== 'github-ae'
)
return next()
const ghaeReleaseNotes = req.context.site.data['release-notes']['github-ae']
// internalLatestRelease is set in lib/all-versions, e.g., '3.5' but UI still displays '@latest'.

View File

@@ -91,7 +91,7 @@ describe('browser search', () => {
newPage.on('request', (interceptedRequest) => {
if (
interceptedRequest.method() === 'GET' &&
/api\/search\/legacy\?/i.test(interceptedRequest.url())
/api\/search\/v1\?/i.test(interceptedRequest.url())
) {
const { searchParams } = new URL(interceptedRequest.url())
expect(searchParams.get('version')).toBe('dotcom')
@@ -119,7 +119,7 @@ describe('browser search', () => {
newPage.on('request', (interceptedRequest) => {
if (
interceptedRequest.method() === 'GET' &&
/api\/search\/legacy\?/i.test(interceptedRequest.url())
/api\/search\/v1\?/i.test(interceptedRequest.url())
) {
const { searchParams } = new URL(interceptedRequest.url())
expect(searchParams.get('version')).toBe('ghae')

View File

@@ -57,8 +57,11 @@ describeIfElasticsearchURL('search v1 middleware', () => {
expect(hit.url).toBe('/en/foo')
expect(hit.title).toBe('Foo')
expect(hit.breadcrumbs).toBe('fooing')
// By default, 'title' and 'content' is included in highlights,
// but not 'headings'
expect(hit.highlights.title[0]).toBe('<mark>Foo</mark>')
expect(hit.highlights.content[0]).toMatch('<mark>foo</mark>')
expect(hit.highlights.headings).toBeUndefined()
// Check that it can be cached at the CDN
expect(res.headers['set-cookie']).toBeUndefined()
@@ -83,6 +86,36 @@ describeIfElasticsearchURL('search v1 middleware', () => {
expect(hit.es_url).toBeTruthy()
})
test('search with and without autocomplete on', async () => {
// *Without* autocomplete=true
{
const sp = new URLSearchParams()
sp.set('query', 'sill')
const res = await get('/api/search/v1?' + sp)
expect(res.statusCode).toBe(200)
const results = JSON.parse(res.text)
// Fixtures contains no word called 'sill'. It does contain the term
// 'silly' which, in English, becomes 'silli` when stemmed.
// Because we don't use `&autocomplete=true` this time, we expect
// to find nothing.
expect(results.meta.found.value).toBe(0)
}
// *With* autocomplete=true
{
const sp = new URLSearchParams()
sp.set('query', 'sill')
sp.set('autocomplete', 'true')
const res = await get('/api/search/v1?' + sp)
expect(res.statusCode).toBe(200)
const results = JSON.parse(res.text)
expect(results.meta.found.value).toBeGreaterThanOrEqual(1)
const hit = results.hits[0]
const contentHighlights = hit.highlights.content
expect(contentHighlights[0]).toMatch('<mark>silly</mark>')
}
})
test('find nothing', async () => {
const sp = new URLSearchParams()
sp.set('query', 'xojixjoiwejhfoiuwehjfioweufhj')
@@ -93,6 +126,38 @@ describeIfElasticsearchURL('search v1 middleware', () => {
expect(results.meta.found.value).toBe(0)
})
test('configurable highlights', async () => {
const sp = new URLSearchParams()
sp.set('query', 'introduction heading')
sp.append('highlights', 'headings')
sp.append('highlights', 'content')
const res = await get('/api/search/v1?' + sp)
expect(res.statusCode).toBe(200)
const results = JSON.parse(res.text)
expect(results.meta.found.value).toBeGreaterThanOrEqual(1)
for (const hit of results.hits) {
expect(hit.highlights.title).toBeFalsy()
expect(hit.highlights.headings).toBeTruthy()
expect(hit.highlights.content).toBeTruthy()
}
})
test('highlights keys matches highlights configuration', async () => {
const sp = new URLSearchParams()
// This will match because it's in the 'content' but not in 'headings'
sp.set('query', 'Fact of life')
sp.set('highlights', 'headings')
const res = await get('/api/search/v1?' + sp)
expect(res.statusCode).toBe(200)
const results = JSON.parse(res.text)
expect(results.meta.found.value).toBeGreaterThanOrEqual(1)
for (const hit of results.hits) {
expect(hit.highlights.headings).toBeTruthy()
expect(hit.highlights.title).toBeFalsy()
expect(hit.highlights.content).toBeFalsy()
}
})
test('version can be aliased', async () => {
const sp = new URLSearchParams()
sp.set('query', 'foo')
@@ -169,6 +234,15 @@ describeIfElasticsearchURL('search v1 middleware', () => {
expect(res.statusCode).toBe(400)
expect(JSON.parse(res.text).error).toMatch('sort')
}
// unrecognized highlights
{
const sp = new URLSearchParams()
sp.set('query', 'test')
sp.set('highlights', 'neverheardof')
const res = await get('/api/search/v1?' + sp)
expect(res.statusCode).toBe(400)
expect(JSON.parse(res.text).error).toMatch('neverheardof')
}
})
test('breadcrumbless records should always return a string', async () => {

View File

@@ -3,7 +3,7 @@
"objectID": "/en/foo",
"breadcrumbs": "fooing",
"title": "Foo",
"headings": "",
"headings": "Heading 1",
"content": "This is a fixture with the silly foo word.",
"topics": ["Test", "Fixture"],
"popularity": 0.5
@@ -12,7 +12,7 @@
"objectID": "/en/bar",
"breadcrumbs": "baring",
"title": "Bar",
"headings": "",
"headings": "Sub-heading This Sub-heading That",
"content": "Can't have foo if you don't also have bar.",
"topics": ["Test", "Fixture", "Get started"],
"popularity": 0.6
@@ -21,7 +21,7 @@
"objectID": "/en/bar",
"breadcrumbs": "",
"title": "Breadcrumbless",
"headings": "",
"headings": "Introduction",
"content": "Not all pages have a breadcrumb. Fact of life.",
"topics": [],
"popularity": 0.1