1
0
mirror of synced 2025-12-21 02:46:50 -05:00

Merge branch 'main' of github.com:github/docs-internal into script-to-use-short-versions

This commit is contained in:
Sarah Schneider
2021-06-24 10:14:50 -04:00
89 changed files with 1063 additions and 435 deletions

16
.devcontainer/Dockerfile Normal file
View File

@@ -0,0 +1,16 @@
# See here for image contents: https://github.com/microsoft/vscode-dev-containers/tree/v0.177.0/containers/javascript-node/.devcontainer/base.Dockerfile
# [Choice] Node.js version: 16, 14, 12
ARG VARIANT="16-buster"
FROM mcr.microsoft.com/vscode/devcontainers/javascript-node:0-${VARIANT}
# [Optional] Uncomment this section to install additional OS packages.
# RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \
# && apt-get -y install --no-install-recommends <your-package-list-here>
# [Optional] Uncomment if you want to install an additional version of node using nvm
# ARG EXTRA_NODE_VERSION=10
# RUN su node -c "source /usr/local/share/nvm/nvm.sh && nvm install ${EXTRA_NODE_VERSION}"
# [Optional] Uncomment if you want to install more global node modules
# RUN su node -c "npm install -g <your-package-list-here>"

View File

@@ -0,0 +1,35 @@
// For format details, see https://aka.ms/devcontainer.json. For config options, see the README at:
// https://github.com/microsoft/vscode-dev-containers/tree/v0.177.0/containers/javascript-node
{
"name": "docs.github.com",
"build": {
"dockerfile": "Dockerfile",
// Update 'VARIANT' to pick a Node version: 12, 14, 16
"args": { "VARIANT": "16" }
},
// Set *default* container specific settings.json values on container create.
"settings": {
"terminal.integrated.shell.linux": "/bin/bash",
"cSpell.language": ",en"
},
// Visual Studio Code extensions which help authoring for docs.github.com.
"extensions": [
"dbaeumer.vscode-eslint",
"sissel.shopify-liquid",
"davidanson.vscode-markdownlint",
"bierner.markdown-preview-github-styles",
"yzhang.markdown-all-in-one",
"streetsidesoftware.code-spell-checker"
],
// Use 'forwardPorts' to make a list of ports inside the container available locally.
"forwardPorts": [4000],
// Use 'postCreateCommand' to run commands after the container is created.
"postCreateCommand": "npm ci && npm run build",
// Comment out connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root.
"remoteUser": "node"
}

View File

@@ -15,7 +15,9 @@ module.exports = {
ecmaVersion: 11
},
rules: {
'import/no-extraneous-dependencies': ['error']
'import/no-extraneous-dependencies': ['error'],
'node/global-require': ['error'],
'import/no-dynamic-require': ['error']
},
overrides: [
{

View File

@@ -63,7 +63,7 @@ jobs:
owner: 'github',
repo: process.env.TEAM_CONTENT_REPO,
title: `@${context.payload.sender.login} confirm that \#${issueNo} should be in the public github/docs repo`,
body: `@${context.payload.sender.login} opened https://github.com/github/docs/issues/${issueNo} publicly in the github/docs repo, instead of the private github/docs-internal repo.\n\n@${context.payload.sender.login}, please confirm that this belongs in the public repo and that no sensitive information was disclosed by commenting below and closing the issue.\n\nIf this was not intentional and sensitive information was shared, please delete https://github.com/github/docs/issues/${issueNo} and notify us in the \#docs-open-source channel.\n\nThanks!`,
body: `@${context.payload.sender.login} opened https://github.com/github/docs/issues/${issueNo} publicly in the github/docs repo, instead of the private github/${process.env.TEAM_CONTENT_REPO} repo.\n\n@${context.payload.sender.login}, please confirm that this belongs in the public repo and that no sensitive information was disclosed by commenting below and closing the issue.\n\nIf this was not intentional and sensitive information was shared, please delete https://github.com/github/docs/issues/${issueNo} and notify us in the \#docs-open-source channel.\n\nThanks!`,
labels: ['OS confirmation'],
});
@@ -75,4 +75,4 @@ jobs:
with:
channel: ${{ secrets.DOCS_OPEN_SOURCE_SLACK_CHANNEL_ID }}
bot-token: ${{ secrets.SLACK_DOCS_BOT_TOKEN }}
text: <@${{github.actor}}> opened https://github.com/github/docs/issues/${{ github.event.number || github.event.issue.number }} publicly on the github/docs repo instead of the private github/docs-internal repo. They have been notified via a new issue in the github/docs-internal repo to confirm this was intentional.
text: <@${{github.actor}}> opened https://github.com/github/docs/issues/${{ github.event.number || github.event.issue.number }} publicly on the github/docs repo instead of a private repo. They have been notified via a new issue in the private repo to confirm this was intentional.

View File

@@ -52,6 +52,9 @@ COPY tsconfig.json ./tsconfig.json
RUN npx tsc
# We need to copy data in order to do the build
COPY --chown=node:node data ./data
RUN npm run build
# --------------------------------------------------------------------------------
@@ -85,7 +88,6 @@ ENV AIRGAP true
# Copy only what's needed to run the server
COPY --chown=node:node assets ./assets
COPY --chown=node:node content ./content
COPY --chown=node:node data ./data
COPY --chown=node:node includes ./includes
COPY --chown=node:node layouts ./layouts
COPY --chown=node:node lib ./lib

View File

Before

Width:  |  Height:  |  Size: 80 KiB

After

Width:  |  Height:  |  Size: 80 KiB

View File

@@ -19,7 +19,7 @@ You must accept the terms of service to publish actions in {% data variables.pro
Before you can publish an action, you'll need to create an action in your repository. For more information, see "[Creating actions](/actions/creating-actions)."
When you plan to publish your action to {% data variables.product.prodname_marketplace %}, you'll need ensure that the repository only includes the metadata file, code, and files necessary for the action. Creating a single repository for the action allows you tag, release, and package the code in a single unit. {% data variables.product.prodname_dotcom %} also uses the action's metadata on your {% data variables.product.prodname_marketplace %} page.
When you plan to publish your action to {% data variables.product.prodname_marketplace %}, you'll need ensure that the repository only includes the metadata file, code, and files necessary for the action. Creating a single repository for the action allows you to tag, release, and package the code in a single unit. {% data variables.product.prodname_dotcom %} also uses the action's metadata on your {% data variables.product.prodname_marketplace %} page.
Actions are published to {% data variables.product.prodname_marketplace %} immediately and aren't reviewed by {% data variables.product.prodname_dotcom %} as long as they meet these requirements:

View File

@@ -39,7 +39,7 @@ We recommend that you have a basic understanding of Xamarin, .NET Core SDK, YAML
- "[Getting started with .NET](https://dotnet.microsoft.com/learn)"
- "[Learn Xamarin](https://dotnet.microsoft.com/learn/xamarin)"
## Bulding Xamarin.iOS apps
## Building Xamarin.iOS apps
The example below demonstrates how to change the default Xamarin SDK versions and build a Xamarin.iOS application.
@@ -79,7 +79,7 @@ jobs:
```
{% endraw %}
## Bulding Xamarin.Android apps
## Building Xamarin.Android apps
The example below demonstrates how to change default Xamarin SDK versions and build a Xamarin.Android application.

View File

@@ -100,12 +100,12 @@ jobs:
echo -n "$BUILD_PROVISION_PROFILE_BASE64" | base64 --decode --output $PP_PATH
# create temporary keychain
security create-keychain -p $KEYCHAIN_PASSWORD $KEYCHAIN_PATH
security create-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH
security set-keychain-settings -lut 21600 $KEYCHAIN_PATH
security unlock-keychain -p $KEYCHAIN_PASSWORD $KEYCHAIN_PATH
security unlock-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH
# import certificate to keychain
security import $CERTIFICATE_PATH -P $P12_PASSWORD -A -t cert -f pkcs12 -k $KEYCHAIN_PATH
security import $CERTIFICATE_PATH -P "$P12_PASSWORD" -A -t cert -f pkcs12 -k $KEYCHAIN_PATH
security list-keychain -d user -s $KEYCHAIN_PATH
# apply provisioning profile

View File

@@ -9,7 +9,6 @@ versions:
---
{% data reusables.actions.enterprise-beta %}
{% data reusables.actions.visualization-beta %}
{% data reusables.actions.enterprise-github-hosted-runners %}
{% data reusables.actions.ae-beta %}

View File

@@ -439,7 +439,7 @@ Creates a hash for any `package-lock.json` and `Gemfile.lock` files in the repos
## Job status check functions
You can use the following status check functions as expressions in `if` conditionals. If your `if` expression does not contain any of the status functions it will automatically result with `success()`. For more information about `if` conditionals, see "[Workflow syntax for GitHub Actions](/articles/workflow-syntax-for-github-actions/#jobsjob_idif)."
You can use the following status check functions as expressions in `if` conditionals. A default status check of `success()` is applied unless you include one of these functions. For more information about `if` conditionals, see "[Workflow syntax for GitHub Actions](/articles/workflow-syntax-for-github-actions/#jobsjob_idif)."
### success

View File

@@ -110,7 +110,7 @@ We strongly recommend enabling PROXY support for both your instance and the load
{% data reusables.enterprise_clustering.x-forwarded-for %}
To enable the `X-Fowarded-For` header, use this command:
To enable the `X-Forwarded-For` header, use this command:
```shell
$ ghe-config 'loadbalancer.http-forward' 'true' && ghe-cluster-config-apply

View File

@@ -1,6 +1,6 @@
---
title: GitHub Enterprise Documentation
shortTitle: GitHub Enterprise
title: Enterprise administrator documentation
shortTitle: Enterprise administrators
intro: 'Documentation and guides for enterprise administrators, system administrators, and security specialists who {% if enterpriseServerVersions contains currentVersion %}deploy, {% endif %}configure{% if enterpriseServerVersions contains currentVersion %},{% endif %} and manage {% data variables.product.product_name %}.'
introLinks:
overview: '{% if enterpriseServerVersions contains currentVersion %}/admin/overview/system-overview{% elsif currentVersion == "github-ae@latest" %}/admin/overview/about-github-ae{% endif %}'

View File

@@ -34,7 +34,7 @@ As a maintainer, it's good practice to clearly indicate how and where you want t
Maintainers should disclose vulnerabilities in a timely manner. If there is a security vulnerability in your repository, we recommend you:
- Treat the vulnerability as a security issue rather than a simple bug, both in your response and your disclosure. For example, you'll need to explicitly mention that the issue is a security vulnerability in the release notes.
- Acknowlege receipt of the vulnerability report as quickly as possible, even if no immediate resources are available for investigation. This sends the message that you are quick to respond and act, and it sets a positive tone for the rest of the interaction between you and the vulnerability reporter.
- Acknowledge receipt of the vulnerability report as quickly as possible, even if no immediate resources are available for investigation. This sends the message that you are quick to respond and act, and it sets a positive tone for the rest of the interaction between you and the vulnerability reporter.
- Involve the vulnerability reporter when you verify the impact and veracity of the report. It's likely the vulnerability reporter has already spent time considering the vulnerability in a variety of scenarios, some of which you may have not considered yourself.
- Remediate the issue in a way that you see fit, taking any concerns and advice provided by the vulnerability reporter into careful consideration. Often the vulnerability reporter will have knowledge of certain corner cases and remediation bypasses that are easy to miss without a security research background.
- Always acknowledge the vulnerability reporter when you credit the discovery.
@@ -65,5 +65,3 @@ The process for reporting and disclosing vulnerabilities for projects on {% data
To get started, see "[Creating a security advisory](/github/managing-security-vulnerabilities/creating-a-security-advisory)."

View File

@@ -31,7 +31,7 @@ You can access the Command Palette in a number of ways.
To see all commands related to {% data variables.product.prodname_github_codespaces %} access the Command Palette, then start typing "Codespaces".
![A list of all commands that relate to Codespaces](/assets/images/help/codespaces/codespaces-commad-palette.png)
![A list of all commands that relate to Codespaces](/assets/images/help/codespaces/codespaces-command-palette.png)
### Suspending or stopping a codespace

View File

@@ -26,7 +26,7 @@ Codespaces are associated with a specific branch of a repository and the reposit
When you create a codespace, a number of steps happen to enable full access to your development environment.
- Resources such as a VM and storage for your container are assigned. A new VM is created every time you create or start a codespace to ensure that you always have the latest versions and security patches.
- {% data variables.product.prodname_codespaces %} recieves information about your repository, branch, commits, your public dotfiles repository, and any secrets that you have created.
- {% data variables.product.prodname_codespaces %} receives information about your repository, branch, commits, your public dotfiles repository, and any secrets that you have created.
- {% data variables.product.prodname_codespaces %} executes a shallow clone of the repository.
- If you have one in your repository, {% data variables.product.prodname_codespaces %} runs the `devcontainer.json` file. For more information, see "[Configuring {% data variables.product.prodname_codespaces %} for your project](/github/developing-online-with-codespaces/configuring-codespaces-for-your-project)."
- Your Docker container, `docker-compose`, or other initialization is run.
@@ -56,6 +56,3 @@ When you create a codespace, a number of steps happen to enable full access to y
4. To create a codespace using a Standard machine type, click {% octicon "plus" aria-label="The plus icon" %} **New codespace**.
![New codespace button](/assets/images/help/codespaces/new-codespace-button.png)

View File

@@ -22,7 +22,7 @@ When an application running inside a codespace outputs a port to the console, {%
![Automatic port forwarding](/assets/images/help/codespaces/automatic-port-forwarding.png)
You can also forward a port manually, label forwarded ports, share fowarded ports publicly, and add forwarded ports to the codespace configuration.
You can also forward a port manually, label forwarded ports, share forwarded ports publicly, and add forwarded ports to the codespace configuration.
## Forwarding a port
@@ -58,10 +58,9 @@ If you want to share a forwarded port with others, you can make the port public.
You can add a forwarded port to the {% data variables.product.prodname_codespaces %} configuration for the repository, so the port will automatically be forwarded for all codespaces created from the repository. After you update the configuration, any previously created codespaces must be rebuilt for the change to apply. For more information, see "[Configuring {% data variables.product.prodname_codespaces %} for your project](/codespaces/setting-up-your-codespace/configuring-codespaces-for-your-project#applying-changes-to-your-configuration)."
You can manually configure fowarded ports in a `.devcontainer.json` file using the `forwardPorts` property, or you can use the "Ports" panel in your codespace.
You can manually configure forwarded ports in a `.devcontainer.json` file using the `forwardPorts` property, or you can use the "Ports" panel in your codespace.
{% data reusables.codespaces.navigate-to-ports-tab %}
1. Right click the port you want to add to the codespace configuration, then click **Set Label and Update devcontainer.json**.
![Option to set label and add port to devcontainer.json in the right-click menu](/assets/images/help/codespaces/update-devcontainer-to-add-port-option.png)
{% data reusables.codespaces.type-port-label %}

View File

@@ -166,7 +166,7 @@ You can use the Dockerfile to add additional container layers to specify OS pack
## Step 3: Modify your devcontainer.json file
With your dev container added and a basic understanding of what everything does, you can now make changes to configure it for your environment. In this example, you'll add properties to install extensions and restore your project dependancies when your codespace launches.
With your dev container added and a basic understanding of what everything does, you can now make changes to configure it for your environment. In this example, you'll add properties to install extensions and restore your project dependencies when your codespace launches.
1. In the Explorer, expand the `.devcontainer` folder and select the `devcontainer.json` file from the tree to open it.

View File

@@ -148,7 +148,7 @@ You can use the Dockerfile to add additional container layers to specify OS pack
With your dev container added and a basic understanding of what everything does, you can now make changes to configure it for your environment. In this example, you'll add properties to install extensions and your project dependencies when your codespace launches.
1. In the Explorer, select the `devcontainer.json` file from the tree to open it. You might have to exand the `.devcontainer` folder to see it.
1. In the Explorer, select the `devcontainer.json` file from the tree to open it. You might have to expand the `.devcontainer` folder to see it.
!["Codespaces: Rebuild Container" in the command palette](/assets/images/help/codespaces/devcontainers-options.png)

View File

@@ -132,7 +132,7 @@ You can use the Dockerfile to add additional container layers to specify OS pack
With your dev container added and a basic understanding of what everything does, you can now make changes to configure it for your environment. In this example, you'll add properties to install npm when your codespace launches and make a list of ports inside the container available locally.
1. In the Explorer, select the `devcontainer.json` file from the tree to open it. You might have to exand the `.devcontainer` folder to see it.
1. In the Explorer, select the `devcontainer.json` file from the tree to open it. You might have to expand the `.devcontainer` folder to see it.
!["Codespaces: Rebuild Container" in the command palette](/assets/images/help/codespaces/devcontainers-options.png)

View File

@@ -46,7 +46,7 @@ To set up your project with a custom container, you will need to use a `devconta
{% data reusables.codespaces.command-palette-container %}
2. For this example, click **Python 3**. If you need additional features you can select any container thats specific to Python or a combination of tools such as Python 3 and PostgresSQL.
2. For this example, click **Python 3**. If you need additional features you can select any container thats specific to Python or a combination of tools such as Python 3 and PostgreSQL.
![Select Python option from the list](/assets/images/help/codespaces/add-python-prebuilt-container.png)
3. Click the recommended version of Python.
![Python version selection](/assets/images/help/codespaces/add-python-version.png)
@@ -155,7 +155,7 @@ You can use the Dockerfile to add additional container layers to specify OS pack
## Step 3: Modify your devcontainer.json file
With your dev container added and a basic understanding of what everything does, you can now make changes to configure it for your environment. In this example, you'll add properties to install extensions and your project dependancies when your codespace launches.
With your dev container added and a basic understanding of what everything does, you can now make changes to configure it for your environment. In this example, you'll add properties to install extensions and your project dependencies when your codespace launches.
1. In the Explorer, expand the `.devcontainer` folder and select the `devcontainer.json` file from the tree to open it.

View File

@@ -1,6 +1,6 @@
---
title: Managing Codespaces for your organization
intro: 'You can manange and review how users in your organization can use {% data variables.product.prodname_github_codespaces %}.'
intro: 'You can manage and review how users in your organization can use {% data variables.product.prodname_github_codespaces %}.'
versions:
free-pro-team: '*'
topics:

View File

@@ -18,4 +18,4 @@ When you perform an action related to {% data variables.product.prodname_codespa
![security log with Codespaces information](/assets/images/help/settings/codespaces-audit-log.png)
The security log includes details on what action occured and when you performed it. For information about {% data variables.product.prodname_codespaces %} actions, see "[{% data variables.product.prodname_codespaces %} category actions](/github/authenticating-to-github/reviewing-your-security-log#codespaces-category-actions)".
The security log includes details on what action occurred and when you performed it. For information about {% data variables.product.prodname_codespaces %} actions, see "[{% data variables.product.prodname_codespaces %} category actions](/github/authenticating-to-github/reviewing-your-security-log#codespaces-category-actions)".

View File

@@ -19,7 +19,7 @@ Squashing allows you to combine multiple commits in your branch's history into a
4. Select the commits to squash and drop them on the commit you want to combine them with. You can select one commit or select multiple commits using <kbd>⌘</kbd> or <kbd>Shift</kbd>.
![squash drag and drop](/assets/images/help/desktop/squash-drag-and-drop.png)
5. Modify the commit message of your new commit. The commit messages of the selected commits you want to squash are pre-filled into the **Summary** and **Description** fields.
6. Click **Squash Commmits**.
6. Click **Squash Commits**.
{% endmac %}
@@ -31,7 +31,7 @@ Squashing allows you to combine multiple commits in your branch's history into a
4. Select the commits to squash and drop them on the commit you want to combine them with. You can select one commit or select multiple commits using <kbd>Ctrl</kbd> or <kbd>Shift</kbd>.
![squash drag and drop](/assets/images/help/desktop/squash-drag-and-drop.png)
5. Modify the commit message of your new commit. The commit messages of the selected commits you want to squash are pre-filled into the **Summary** and **Description** fields.
6. Click **Squash Commmits**.
6. Click **Squash Commits**.
{% endwindows %}

View File

@@ -169,7 +169,7 @@ Open up the `template_server.rb` file in your favorite text editor. You'll see c
At the top of the file you'll see `set :port 3000`, which sets the port used when starting the web server to match the port you redirected your webhook payloads to in "[Step 1. Start a new Smee channel](#step-1-start-a-new-smee-channel)."
The next code you'll see is the `class GHApp < Sintra::Application` declaration. You'll write all of the code for your GitHub App inside this class.
The next code you'll see is the `class GHApp < Sinatra::Application` declaration. You'll write all of the code for your GitHub App inside this class.
Out of the box, the class in the template does the following things:
* [Read the environment variables](#read-the-environment-variables)

View File

@@ -94,7 +94,7 @@ You can set a status to display information about your current availability on {
When you set your status, you can also let people know that you have limited availability on {% data variables.product.product_name %}.
![At-mentioned username shows "busy" note next to username](/assets/images/help/profile/username-with-limited-availibilty-text.png)
![At-mentioned username shows "busy" note next to username](/assets/images/help/profile/username-with-limited-availability-text.png)
![Requested reviewer shows "busy" note next to username](/assets/images/help/profile/request-a-review-limited-availability-status.png)

View File

@@ -194,7 +194,7 @@ For the purposes of Article 46 (2) of the General Data Protection Regulation (EU
(e) 'the applicable data protection law' means the legislation protecting the fundamental rights and freedoms of individuals and, in particular, their right to privacy with respect to the processing of personal data applicable to a data controller in the Member State in which the data exporter is established;
(f) 'technical and organisational security measures' means those measures aimed at protecting personal data against accidental or unlawful destruction or accidental loss, alteration, unauthorised disclosure or access, in particular where the processing involves the transmission of data over a network, and against all other unlawful forms of processing.
(f) 'technical and organizational security measures' means those measures aimed at protecting personal data against accidental or unlawful destruction or accidental loss, alteration, unauthorized disclosure or access, in particular where the processing involves the transmission of data over a network, and against all other unlawful forms of processing.
#### Clause 2: Details of the transfer
The details of the transfer and in particular the special categories of personal data where applicable are specified in Appendix 1 below which forms an integral part of the Clauses.
@@ -215,9 +215,9 @@ The data exporter agrees and warrants:
(b) that it has instructed and throughout the duration of the personal data processing services will instruct the data importer to process the personal data transferred only on the data exporter's behalf and in accordance with the applicable data protection law and the Clauses;
(c) that the data importer will provide sufficient guarantees in respect of the technical and organisational security measures specified in Appendix 2 below;
(c) that the data importer will provide sufficient guarantees in respect of the technical and organizational security measures specified in Appendix 2 below;
(d) that after assessment of the requirements of the applicable data protection law, the security measures are appropriate to protect personal data against accidental or unlawful destruction or accidental loss, alteration, unauthorised disclosure or access, in particular where the processing involves the transmission of data over a network, and against all other unlawful forms of processing, and that these measures ensure a level of security appropriate to the risks presented by the processing and the nature of the data to be protected having regard to the state of the art and the cost of their implementation;
(d) that after assessment of the requirements of the applicable data protection law, the security measures are appropriate to protect personal data against accidental or unlawful destruction or accidental loss, alteration, unauthorized disclosure or access, in particular where the processing involves the transmission of data over a network, and against all other unlawful forms of processing, and that these measures ensure a level of security appropriate to the risks presented by the processing and the nature of the data to be protected having regard to the state of the art and the cost of their implementation;
(e) that it will ensure compliance with the security measures;
@@ -238,15 +238,15 @@ The data importer agrees and warrants:
(b) that it has no reason to believe that the legislation applicable to it prevents it from fulfilling the instructions received from the data exporter and its obligations under the contract and that in the event of a change in this legislation which is likely to have a substantial adverse effect on the warranties and obligations provided by the Clauses, it will promptly notify the change to the data exporter as soon as it is aware, in which case the data exporter is entitled to suspend the transfer of data and/or terminate the contract;
(c) that it has implemented the technical and organisational security measures specified in Appendix 2 before processing the personal data transferred;
(c) that it has implemented the technical and organizational security measures specified in Appendix 2 before processing the personal data transferred;
(d) that it will promptly notify the data exporter about:
(i) any legally binding request for disclosure of the personal data by a law enforcement authority unless otherwise prohibited, such as a prohibition under criminal law to preserve the confidentiality of a law enforcement investigation,
(ii) any accidental or unauthorised access, and
(ii) any accidental or unauthorized access, and
(iii) any request received directly from the data subjects without responding to that request, unless it has been otherwise authorised to do so;
(iii) any request received directly from the data subjects without responding to that request, unless it has been otherwise authorized to do so;
(e) to deal promptly and properly with all inquiries from the data exporter relating to its processing of the personal data subject to the transfer and to abide by the advice of the supervisory authority with regard to the processing of the data transferred;
@@ -404,4 +404,3 @@ c. Any third party auditor must disclose to GitHub any findings and recommended
d. In the event of a regulatory audit, access will be permitted only during regular business hours, Pacific time.
e. To the extent permitted by law, Customer must keep confidential any information gathered through any such audit of GitHub that, by its nature, should be confidential.

View File

@@ -31,7 +31,7 @@ The DMCA also prohibits the [circumvention of technical measures](https://www.co
## DMCA Notices In a Nutshell
The DMCA provides two simple, straightforward procedures that all GitHub users should know about: (i) a [takedown-notice](/articles/guide-to-submitting-a-dmca-takedown-notice) procedure for copyright holders to request that content be removed; and (ii) a [counter-notice](/articles/guide-to-submitting-a-dmca-counter-notice) procedure for users to get content reenabled when content is taken down by mistake or misidentification.
The DMCA provides two simple, straightforward procedures that all GitHub users should know about: (i) a [takedown-notice](/articles/guide-to-submitting-a-dmca-takedown-notice) procedure for copyright holders to request that content be removed; and (ii) a [counter-notice](/articles/guide-to-submitting-a-dmca-counter-notice) procedure for users to get content re-enabled when content is taken down by mistake or misidentification.
DMCA [takedown notices](/articles/guide-to-submitting-a-dmca-takedown-notice) are used by copyright owners to ask GitHub to take down content they believe to be infringing. If you are a software designer or developer, you create copyrighted content every day. If someone else is using your copyrighted content in an unauthorized manner on GitHub, you can send us a DMCA takedown notice to request that the infringing content be changed or removed.
@@ -60,7 +60,7 @@ Here are the basic steps in the process.
7. **User May Send A Counter Notice.** We encourage users who have had content disabled to consult with a lawyer about their options. If a user believes that their content was disabled as a result of a mistake or misidentification, they may send us a [counter notice](/articles/guide-to-submitting-a-dmca-counter-notice). As with the original notice, we will make sure that the counter notice is sufficiently detailed (as explained in the [how-to guide](/articles/guide-to-submitting-a-dmca-counter-notice)). If it is, we will [post it](#d-transparency) to our [public repository](https://github.com/github/dmca) and pass the notice back to the copyright owner by sending them the link.
8. **Copyright Owner May File a Legal Action.** If a copyright owner wishes to keep the content disabled after receiving a counter notice, they will need to initiate a legal action seeking a court order to restrain the user from engaging in infringing activity relating to the content on GitHub. In other words, you might get sued. If the copyright owner does not give GitHub notice within 10-14 days, by sending a copy of a valid legal complaint filed in a court of competent jurisdiction, GitHub will reenable the disabled content.
8. **Copyright Owner May File a Legal Action.** If a copyright owner wishes to keep the content disabled after receiving a counter notice, they will need to initiate a legal action seeking a court order to restrain the user from engaging in infringing activity relating to the content on GitHub. In other words, you might get sued. If the copyright owner does not give GitHub notice within 10-14 days, by sending a copy of a valid legal complaint filed in a court of competent jurisdiction, GitHub will re-enable the disabled content.
## B. What About Forks? (or What's a Fork?)

View File

@@ -193,7 +193,7 @@ For the purposes of Article 46(2) of the General Data Protection Regulation (EU
(e) 'the applicable data protection law' means the legislation protecting the fundamental rights and freedoms of individuals and, in particular, their right to privacy with respect to the processing of personal data applicable to a data controller in the Member State in which the data exporter is established;
(f) 'technical and organisational security measures' means those measures aimed at protecting personal data against accidental or unlawful destruction or accidental loss, alteration, unauthorised disclosure or access, in particular where the processing involves the transmission of data over a network, and against all other unlawful forms of processing.
(f) 'technical and organizational security measures' means those measures aimed at protecting personal data against accidental or unlawful destruction or accidental loss, alteration, unauthorized disclosure or access, in particular where the processing involves the transmission of data over a network, and against all other unlawful forms of processing.
#### Clause 2: Details of the transfer
The details of the transfer and in particular the special categories of personal data where applicable are specified in Appendix 1 below which forms an integral part of the Clauses.
@@ -214,9 +214,9 @@ The data exporter agrees and warrants:
(b) that it has instructed and throughout the duration of the personal data processing services will instruct the data importer to process the personal data transferred only on the data exporter's behalf and in accordance with the applicable data protection law and the Clauses;
(c) that the data importer will provide sufficient guarantees in respect of the technical and organisational security measures specified in Appendix 2 below;
(c) that the data importer will provide sufficient guarantees in respect of the technical and organizational security measures specified in Appendix 2 below;
(d) that after assessment of the requirements of the applicable data protection law, the security measures are appropriate to protect personal data against accidental or unlawful destruction or accidental loss, alteration, unauthorised disclosure or access, in particular where the processing involves the transmission of data over a network, and against all other unlawful forms of processing, and that these measures ensure a level of security appropriate to the risks presented by the processing and the nature of the data to be protected having regard to the state of the art and the cost of their implementation;
(d) that after assessment of the requirements of the applicable data protection law, the security measures are appropriate to protect personal data against accidental or unlawful destruction or accidental loss, alteration, unauthorized disclosure or access, in particular where the processing involves the transmission of data over a network, and against all other unlawful forms of processing, and that these measures ensure a level of security appropriate to the risks presented by the processing and the nature of the data to be protected having regard to the state of the art and the cost of their implementation;
(e) that it will ensure compliance with the security measures;
@@ -237,15 +237,15 @@ The data importer agrees and warrants:
(b) that it has no reason to believe that the legislation applicable to it prevents it from fulfilling the instructions received from the data exporter and its obligations under the contract and that in the event of a change in this legislation which is likely to have a substantial adverse effect on the warranties and obligations provided by the Clauses, it will promptly notify the change to the data exporter as soon as it is aware, in which case the data exporter is entitled to suspend the transfer of data and/or terminate the contract;
(c) that it has implemented the technical and organisational security measures specified in Appendix 2 before processing the personal data transferred;
(c) that it has implemented the technical and organizational security measures specified in Appendix 2 before processing the personal data transferred;
(d) that it will promptly notify the data exporter about:
(i) any legally binding request for disclosure of the personal data by a law enforcement authority unless otherwise prohibited, such as a prohibition under criminal law to preserve the confidentiality of a law enforcement investigation,
(ii) any accidental or unauthorised access, and
(ii) any accidental or unauthorized access, and
(iii) any request received directly from the data subjects without responding to that request, unless it has been otherwise authorised to do so;
(iii) any request received directly from the data subjects without responding to that request, unless it has been otherwise authorized to do so;
(e) to deal promptly and properly with all inquiries from the data exporter relating to its processing of the personal data subject to the transfer and to abide by the advice of the supervisory authority with regard to the processing of the data transferred;

View File

@@ -18,7 +18,7 @@ Every gist is a Git repository, which means that it can be forked and cloned. {%
Gists can be {% if currentVersion == "github-ae@latest" %}internal{% else %}public{% endif %} or secret. {% if currentVersion == "github-ae@latest" %}Internal{% else %}Public{% endif %} gists show up in {% data variables.gists.discover_url %}, where {% if currentVersion == "github-ae@latest" %}enterprise members{% else %}people{% endif %} can browse new gists as they're created. They're also searchable, so you can use them if you'd like other people to find and see your work.
Secret gists don't show up in {% data variables.gists.discover_url %} and are not searchable. Secret gists aren't private. If you send the URL of a secret gist to {% if currentVersion == "github-ae@latest" %}another enterprise member{% else %}a friend {% endif %}, they'll be able to see it. However, if {% if currentVersion == "github-ae@latest" %}any other enterpise member{% else %}someone you don't know{% endif %} discovers the URL, they'll also be able to see your gist. If you need to keep your code away from prying eyes, you may want to [create a private repository](/articles/creating-a-new-repository) instead.
Secret gists don't show up in {% data variables.gists.discover_url %} and are not searchable. Secret gists aren't private. If you send the URL of a secret gist to {% if currentVersion == "github-ae@latest" %}another enterprise member{% else %}a friend {% endif %}, they'll be able to see it. However, if {% if currentVersion == "github-ae@latest" %}any other enterprise member{% else %}someone you don't know{% endif %} discovers the URL, they'll also be able to see your gist. If you need to keep your code away from prying eyes, you may want to [create a private repository](/articles/creating-a-new-repository) instead.
{% data reusables.gist.cannot-convert-public-gists-to-secret %}

View File

@@ -409,7 +409,7 @@ For more information on the difference between enums and strings, see the [offic
There is a _lot_ more you can do when forming GraphQL calls. Here are some places to look next:
* [Pagination](https://graphql.github.io/learn/pagination/)
* [Fragments](https://graphql.github.io/learn/queries/#fragments)
* [Inline fragments](https://graphql.github.io/learn/queries/#inline-fragments)
* [Directives](https://graphql.github.io/learn/queries/#directives)
* [Pagination](https://graphql.org/learn/pagination/)
* [Fragments](https://graphql.org/learn/queries/#fragments)
* [Inline fragments](https://graphql.org/learn/queries/#inline-fragments)
* [Directives](https://graphql.org/learn/queries/#directives)

View File

@@ -547,7 +547,7 @@ For more information, see "[Managing the publication of {% data variables.produc
| `create_actions_secret` |Triggered when a {% data variables.product.prodname_actions %} secret is created for a repository. For more information, see "[Creating encrypted secrets for a repository](/actions/reference/encrypted-secrets#creating-encrypted-secrets-for-a-repository)."{% endif %}
| `destroy` | Triggered when [a repository is deleted](/articles/deleting-a-repository).{% if currentVersion == "free-pro-team@latest" %}
| `disable` | Triggered when a repository is disabled (e.g., for [insufficient funds](/articles/unlocking-a-locked-account)).{% endif %}
| `enable` | Triggered when a repository is reenabled.{% if currentVersion == "free-pro-team@latest" or currentVersion ver_gt "enterprise-server@2.21" %}
| `enable` | Triggered when a repository is re-enabled.{% if currentVersion == "free-pro-team@latest" or currentVersion ver_gt "enterprise-server@2.21" %}
| `remove_actions_secret` | Triggered when a {% data variables.product.prodname_actions %} secret is removed.{% endif %}
| `remove_member` | Triggered when a user is [removed from a repository as a collaborator](/articles/removing-a-collaborator-from-a-personal-repository).{% if currentVersion == "free-pro-team@latest" or currentVersion ver_gt "enterprise-server@2.21" %}
| `register_self_hosted_runner` | Triggered when a new self-hosted runner is registered. For more information, see "[Adding a self-hosted runner to a repository](/actions/hosting-your-own-runners/adding-self-hosted-runners#adding-a-self-hosted-runner-to-a-repository)."

View File

@@ -1,7 +1,7 @@
---
title: Setting permissions for adding outside collaborators
intro: 'To protect your organization''s data and the number of paid licenses used in your organization, you can allow only owners to invite outside collaborators to organization repositories.'
product: '{% data reusables.gated-features.restict-add-collaborator %}'
product: '{% data reusables.gated-features.restrict-add-collaborator %}'
redirect_from:
- /articles/restricting-the-ability-to-add-outside-collaborators-to-organization-repositories/
- /articles/setting-permissions-for-adding-outside-collaborators

View File

@@ -30,7 +30,7 @@ For more information, see "[Media types](/rest/overview/media-types)."
#### Response using the custom media type
This example response is from a `GET` request to the `/analyses/{analysis_id}` endpoint, using `application/sarif+json` as the `Accept` header value. The example has had indendation and line breaks added for readability. For more information about this endpoint, see "[Get a {% data variables.product.prodname_code_scanning %} analysis for a repository](#get-a-code-scanning-analysis-for-a-repository)."
This example response is from a `GET` request to the `/analyses/{analysis_id}` endpoint, using `application/sarif+json` as the `Accept` header value. The example has had indentation and line breaks added for readability. For more information about this endpoint, see "[Get a {% data variables.product.prodname_code_scanning %} analysis for a repository](#get-a-code-scanning-analysis-for-a-repository)."
```
{

View File

@@ -38,9 +38,9 @@ GitHub Apps have the `Read-only` metadata permission by default. The metadata pe
- [`GET /orgs/:org/repos`](/rest/reference/repos#list-organization-repositories)
- [`GET /rate_limit`](/rest/reference/rate-limit#get-rate-limit-status-for-the-authenticated-user)
- [`GET /repos/:owner/:repo`](/rest/reference/repos#get-a-repository)
{% if currentVersion == "free-pro-team@latest" %}
{% if currentVersion == "free-pro-team@latest" -%}
- [`GET /repos/:owner/:repo/community/profile`](/rest/reference/repos#get-community-profile-metrics)
{% endif %}
{% endif -%}
- [`GET /repos/:owner/:repo/contributors`](/rest/reference/repos#list-repository-contributors)
- [`GET /repos/:owner/:repo/forks`](/rest/reference/repos#list-forks)
- [`GET /repos/:owner/:repo/languages`](/rest/reference/repos#list-repository-languages)
@@ -122,9 +122,9 @@ _Search_
- [`GET /repos/:owner/:repo/actions/jobs/:job_id/logs`](/rest/reference/actions#download-job-logs-for-a-workflow-run) (:read)
- [`GET /repos/:owner/:repo/actions/runs`](/rest/reference/actions#list-workflow-runs-for-a-repository) (:read)
- [`GET /repos/:owner/:repo/actions/runs/:run_id`](/rest/reference/actions#get-a-workflow-run) (:read)
{% if currentVersion == "free-pro-team@latest" %}
{% if currentVersion == "free-pro-team@latest" -%}
- [`POST /repos/:owner/:repo/actions/runs/:run_id/approve`](/rest/reference/actions#approve-a-workflow-run-for-a-fork-pull-request) (:write)
{% endif %}
{% endif -%}
- [`GET /repos/:owner/:repo/actions/runs/:run_id/artifacts`](/rest/reference/actions#list-workflow-run-artifacts) (:read)
- [`POST /repos/:owner/:repo/actions/runs/:run_id/cancel`](/rest/reference/actions#cancel-a-workflow-run) (:write)
- [`GET /repos/:owner/:repo/actions/runs/:run_id/jobs`](/rest/reference/actions#list-jobs-for-a-workflow-run) (:read)
@@ -141,44 +141,44 @@ _Search_
- [`POST /orgs/:org/repos`](/rest/reference/repos#create-an-organization-repository) (:write)
- [`PATCH /repos/:owner/:repo`](/rest/reference/repos#update-a-repository) (:write)
- [`DELETE /repos/:owner/:repo`](/rest/reference/repos#delete-a-repository) (:write)
{% if currentVersion == "free-pro-team@latest" %}
{% if currentVersion == "free-pro-team@latest" -%}
- [`GET /repos/:owner/:repo/actions/runners/downloads`](/rest/reference/actions#list-runner-applications-for-a-repository) (:read)
- [`GET /repos/:owner/:repo/actions/runners`](/rest/reference/actions#list-self-hosted-runners-for-a-repository) (:read)
- [`GET /repos/:owner/:repo/actions/runners/:runner_id`](/rest/reference/actions#get-a-self-hosted-runner-for-a-repository) (:read)
- [`DELETE /repos/:owner/:repo/actions/runners/:runner_id`](/rest/reference/actions#delete-a-self-hosted-runner-from-a-repository) (:write)
- [`POST /repos/:owner/:repo/actions/runners/registration-token`](/rest/reference/actions#create-a-registration-token-for-a-repository) (:write)
- [`POST /repos/:owner/:repo/actions/runners/remove-token`](/rest/reference/actions#create-a-remove-token-for-a-repository) (:write)
{% endif %}
{% if currentVersion == "free-pro-team@latest" %}
{% endif -%}
{% if currentVersion == "free-pro-team@latest" -%}
- [`PUT /repos/:owner/:repo/automated-security-fixes`](/rest/reference/repos#enable-automated-security-fixes) (:write)
{% endif %}
{% if currentVersion == "free-pro-team@latest" %}
{% endif -%}
{% if currentVersion == "free-pro-team@latest" -%}
- [`DELETE /repos/:owner/:repo/automated-security-fixes`](/rest/reference/repos#disable-automated-security-fixes) (:write)
{% endif %}
{% endif -%}
- [`POST /repos/:owner/:repo/forks`](/rest/reference/repos#create-a-fork) (:write)
{% if currentVersion == "free-pro-team@latest" %}
{% if currentVersion == "free-pro-team@latest" -%}
- [`GET /repos/:owner/:repo/interaction-limits`](/rest/reference/interactions#get-interaction-restrictions-for-a-repository) (:read)
{% endif %}
{% if currentVersion == "free-pro-team@latest" %}
{% endif -%}
{% if currentVersion == "free-pro-team@latest" -%}
- [`PUT /repos/:owner/:repo/interaction-limits`](/rest/reference/interactions#set-interaction-restrictions-for-a-repository) (:write)
{% endif %}
{% if currentVersion == "free-pro-team@latest" %}
{% endif -%}
{% if currentVersion == "free-pro-team@latest" -%}
- [`DELETE /repos/:owner/:repo/interaction-limits`](/rest/reference/interactions#remove-interaction-restrictions-for-a-repository) (:write)
{% endif %}
{% if currentVersion == "free-pro-team@latest" %}
{% endif -%}
{% if currentVersion == "free-pro-team@latest" -%}
- [`GET /repos/:owner/:repo/pages/health`](/rest/reference/repos#get-a-dns-health-check-for-github-pages) (:write)
{% endif %}
{% endif -%}
- [`PUT /repos/:owner/:repo/topics`](/rest/reference/repos#replace-all-repository-topics) (:write)
- [`POST /repos/:owner/:repo/transfer`](/rest/reference/repos#transfer-a-repository) (:write)
{% if currentVersion == "free-pro-team@latest" %}
{% if currentVersion == "free-pro-team@latest" -%}
- [`GET /repos/:owner/:repo/vulnerability-alerts`](/rest/reference/repos#enable-vulnerability-alerts) (:write)
{% endif %}
{% if currentVersion == "free-pro-team@latest" %}
{% endif -%}
{% if currentVersion == "free-pro-team@latest" -%}
- [`PUT /repos/:owner/:repo/vulnerability-alerts`](/rest/reference/repos#enable-vulnerability-alerts) (:write)
{% endif %}
{% if currentVersion == "free-pro-team@latest" %}
{% endif -%}
{% if currentVersion == "free-pro-team@latest" -%}
- [`DELETE /repos/:owner/:repo/vulnerability-alerts`](/rest/reference/repos#disable-vulnerability-alerts) (:write)
{% endif %}
{% endif -%}
- [`PATCH /user/repository_invitations/:invitation_id`](/rest/reference/repos#accept-a-repository-invitation) (:write)
- [`DELETE /user/repository_invitations/:invitation_id`](/rest/reference/repos#decline-a-repository-invitation) (:write)
@@ -212,7 +212,7 @@ _Branches_
- [`POST /repos/:owner/:repo/branches/:branch/protection/restrictions/users`](/rest/reference/repos#add-user-access-restrictions) (:write)
- [`PUT /repos/:owner/:repo/branches/:branch/protection/restrictions/users`](/rest/reference/repos#set-user-access-restrictions) (:write)
- [`DELETE /repos/:owner/:repo/branches/:branch/protection/restrictions/users`](/rest/reference/repos#remove-user-access-restrictions) (:write)
{% if currentVersion == "free-pro-team@latest" or currentVersion ver_gt "enterprise-server@3.0" or currentVersion == "github-ae@latest" %}
{% if currentVersion == "free-pro-team@latest" or currentVersion ver_gt "enterprise-server@3.0" or currentVersion == "github-ae@latest" -%}
- [`POST /repos/:owner/:repo/branches/:branch/rename`](/rest/reference/repos#rename-a-branch) (:write)
{% endif %}
@@ -270,69 +270,69 @@ _Traffic_
### Permission on "contents"
- [`GET /repos/:owner/:repo/:archive_format/:ref`](/rest/reference/repos#download-a-repository-archive) (:read)
{% if currentVersion == "free-pro-team@latest" %}
{% if currentVersion == "free-pro-team@latest" -%}
- [`GET /repos/:owner/:repo/actions/artifacts/:artifact_id`](/rest/reference/actions#get-an-artifact) (:read)
{% endif %}
{% if currentVersion == "free-pro-team@latest" %}
{% endif -%}
{% if currentVersion == "free-pro-team@latest" -%}
- [`DELETE /repos/:owner/:repo/actions/artifacts/:artifact_id`](/rest/reference/actions#delete-an-artifact) (:write)
{% endif %}
{% if currentVersion == "free-pro-team@latest" %}
{% endif -%}
{% if currentVersion == "free-pro-team@latest" -%}
- [`GET /repos/:owner/:repo/actions/artifacts/:artifact_id/zip`](/rest/reference/actions#download-an-artifact) (:read)
{% endif %}
{% if currentVersion == "free-pro-team@latest" %}
{% endif -%}
{% if currentVersion == "free-pro-team@latest" -%}
- [`GET /repos/:owner/:repo/actions/jobs/:job_id`](/rest/reference/actions#get-a-job-for-a-workflow-run) (:read)
{% endif %}
{% if currentVersion == "free-pro-team@latest" %}
{% endif -%}
{% if currentVersion == "free-pro-team@latest" -%}
- [`GET /repos/:owner/:repo/actions/jobs/:job_id/logs`](/rest/reference/actions#download-job-logs-for-a-workflow-run) (:read)
{% endif %}
{% if currentVersion == "free-pro-team@latest" %}
{% endif -%}
{% if currentVersion == "free-pro-team@latest" -%}
- [`GET /repos/:owner/:repo/actions/runs`](/rest/reference/actions#list-workflow-runs-for-a-repository) (:read)
{% endif %}
{% if currentVersion == "free-pro-team@latest" %}
{% endif -%}
{% if currentVersion == "free-pro-team@latest" -%}
- [`GET /repos/:owner/:repo/actions/runs/:run_id`](/rest/reference/actions#get-a-workflow-run) (:read)
{% endif %}
{% if currentVersion == "free-pro-team@latest" %}
{% endif -%}
{% if currentVersion == "free-pro-team@latest" -%}
- [`GET /repos/:owner/:repo/actions/runs/:run_id/artifacts`](/rest/reference/actions#list-workflow-run-artifacts) (:read)
{% endif %}
{% if currentVersion == "free-pro-team@latest" %}
{% endif -%}
{% if currentVersion == "free-pro-team@latest" -%}
- [`POST /repos/:owner/:repo/actions/runs/:run_id/cancel`](/rest/reference/actions#cancel-a-workflow-run) (:write)
{% endif %}
{% if currentVersion == "free-pro-team@latest" %}
{% endif -%}
{% if currentVersion == "free-pro-team@latest" -%}
- [`GET /repos/:owner/:repo/actions/runs/:run_id/jobs`](/rest/reference/actions#list-jobs-for-a-workflow-run) (:read)
{% endif %}
{% if currentVersion == "free-pro-team@latest" %}
{% endif -%}
{% if currentVersion == "free-pro-team@latest" -%}
- [`GET /repos/:owner/:repo/actions/runs/:run_id/logs`](/rest/reference/actions#download-workflow-run-logs) (:read)
{% endif %}
{% if currentVersion == "free-pro-team@latest" %}
{% endif -%}
{% if currentVersion == "free-pro-team@latest" -%}
- [`DELETE /repos/:owner/:repo/actions/runs/:run_id/logs`](/rest/reference/actions#delete-workflow-run-logs) (:write)
{% endif %}
{% if currentVersion == "free-pro-team@latest" %}
{% endif -%}
{% if currentVersion == "free-pro-team@latest" -%}
- [`POST /repos/:owner/:repo/actions/runs/:run_id/rerun`](/rest/reference/actions#re-run-a-workflow) (:write)
{% endif %}
{% if currentVersion == "free-pro-team@latest" %}
{% endif -%}
{% if currentVersion == "free-pro-team@latest" -%}
- [`GET /repos/:owner/:repo/actions/secrets`](/rest/reference/actions#list-repository-secrets) (:write)
{% endif %}
{% if currentVersion == "free-pro-team@latest" %}
{% endif -%}
{% if currentVersion == "free-pro-team@latest" -%}
- [`GET /repos/:owner/:repo/actions/secrets/:name`](/rest/reference/actions#get-a-repository-secret) (:write)
{% endif %}
{% if currentVersion == "free-pro-team@latest" %}
{% endif -%}
{% if currentVersion == "free-pro-team@latest" -%}
- [`PUT /repos/:owner/:repo/actions/secrets/:name`](/rest/reference/actions#create-or-update-a-repository-secret) (:write)
{% endif %}
{% if currentVersion == "free-pro-team@latest" %}
{% endif -%}
{% if currentVersion == "free-pro-team@latest" -%}
- [`DELETE /repos/:owner/:repo/actions/secrets/:name`](/rest/reference/actions#delete-a-repository-secret) (:write)
{% endif %}
{% if currentVersion == "free-pro-team@latest" %}
{% endif -%}
{% if currentVersion == "free-pro-team@latest" -%}
- [`GET /repos/:owner/:repo/actions/secrets/public-key`](/rest/reference/actions#get-a-repository-public-key) (:write)
{% endif %}
{% if currentVersion == "free-pro-team@latest" %}
{% endif -%}
{% if currentVersion == "free-pro-team@latest" -%}
- [`GET /repos/:owner/:repo/actions/workflows`](/rest/reference/actions#list-repository-workflows) (:read)
{% endif %}
{% if currentVersion == "free-pro-team@latest" %}
{% endif -%}
{% if currentVersion == "free-pro-team@latest" -%}
- [`GET /repos/:owner/:repo/actions/workflows/:workflow_id`](/rest/reference/actions#get-a-workflow) (:read)
{% endif %}
{% if currentVersion == "free-pro-team@latest" %}
{% endif -%}
{% if currentVersion == "free-pro-team@latest" -%}
- [`GET /repos/:owner/:repo/actions/workflows/:workflow_id/runs`](/rest/reference/actions#list-workflow-runs) (:read)
{% endif %}
{% endif -%}
- [`GET /repos/:owner/:repo/check-runs/:check_run_id`](/rest/reference/checks#get-a-check-run) (:read)
- [`GET /repos/:owner/:repo/check-runs/:check_run_id/annotations`](/rest/reference/checks#list-check-run-annotations) (:read)
- [`GET /repos/:owner/:repo/check-suites/:check_suite_id`](/rest/reference/checks#get-a-check-suite) (:read)
@@ -345,9 +345,9 @@ _Traffic_
- [`GET /repos/:owner/:repo/community/code_of_conduct`](/rest/reference/codes-of-conduct#get-the-code-of-conduct-for-a-repository) (:read)
- [`GET /repos/:owner/:repo/compare/:base...:head`](/rest/reference/repos#compare-two-commits) (:read)
- [`GET /repos/:owner/:repo/contents/:path`](/rest/reference/repos#get-repository-content) (:read)
{% if currentVersion == "free-pro-team@latest" or currentVersion ver_gt "enterprise-server@2.20" or currentVersion == "github-ae@latest" %}
{% if currentVersion == "free-pro-team@latest" or currentVersion ver_gt "enterprise-server@2.20" or currentVersion == "github-ae@latest" -%}
- [`POST /repos/:owner/:repo/dispatches`](/rest/reference/repos#create-a-repository-dispatch-event) (:write)
{% endif %}
{% endif -%}
- [`POST /repos/:owner/:repo/forks`](/rest/reference/repos#create-a-fork) (:read)
- [`POST /repos/:owner/:repo/merges`](/rest/reference/repos#merge-a-branch) (:write)
- [`PUT /repos/:owner/:repo/pulls/:pull_number/merge`](/rest/reference/pulls#merge-a-pull-request) (:write)
@@ -360,7 +360,7 @@ _Branches_
- [`POST /repos/:owner/:repo/branches/:branch/protection/restrictions/apps`](/rest/reference/repos#add-app-access-restrictions) (:write)
- [`PUT /repos/:owner/:repo/branches/:branch/protection/restrictions/apps`](/rest/reference/repos#set-app-access-restrictions) (:write)
- [`DELETE /repos/:owner/:repo/branches/:branch/protection/restrictions/apps`](/rest/reference/repos#remove-user-access-restrictions) (:write)
{% if currentVersion == "free-pro-team@latest" or currentVersion ver_gt "enterprise-server@3.0" or currentVersion == "github-ae@latest" %}
{% if currentVersion == "free-pro-team@latest" or currentVersion ver_gt "enterprise-server@3.0" or currentVersion == "github-ae@latest" -%}
- [`POST /repos/:owner/:repo/branches/:branch/rename`](/rest/reference/repos#rename-a-branch) (:write)
{% endif %}
@@ -399,14 +399,19 @@ _Import_
_Reactions_
{% if currentVersion == "free-pro-team@latest" or currentVersion ver_gt "enterprise-server@2.20" or currentVersion == "github-ae@latest" %}
- [`DELETE /reactions/:reaction_id`](/rest/reference/reactions#delete-a-reaction-legacy) (:write){% else %}- [`DELETE /reactions/:reaction_id`](/rest/reference/reactions#delete-a-reaction) (:write){% endif %}{% if currentVersion == "free-pro-team@latest" or currentVersion ver_gt "enterprise-server@2.20" or currentVersion == "github-ae@latest" %}
{% if currentVersion == "free-pro-team@latest" or currentVersion ver_gt "enterprise-server@2.20" or currentVersion == "github-ae@latest" -%}
- [`DELETE /reactions/:reaction_id`](/rest/reference/reactions#delete-a-reaction-legacy) (:write)
{% else -%}
- [`DELETE /reactions/:reaction_id`](/rest/reference/reactions#delete-a-reaction) (:write)
{% endif -%}
{% if currentVersion == "free-pro-team@latest" or currentVersion ver_gt "enterprise-server@2.20" or currentVersion == "github-ae@latest" -%}
- [`DELETE /repos/:owner/:repo/comments/:comment_id/reactions/:reaction_id`](/rest/reference/reactions#delete-a-commit-comment-reaction) (:write)
- [`DELETE /repos/:owner/:repo/issues/:issue_number/reactions/:reaction_id`](/rest/reference/reactions#delete-an-issue-reaction) (:write)
- [`DELETE /repos/:owner/:repo/issues/comments/:comment_id/reactions/:reaction_id`](/rest/reference/reactions#delete-an-issue-comment-reaction) (:write)
- [`DELETE /repos/:owner/:repo/pulls/comments/:comment_id/reactions/:reaction_id`](/rest/reference/reactions#delete-a-pull-request-comment-reaction) (:write)
- [`DELETE /orgs/:org/teams/:team_slug/discussions/:discussion_number/reactions/:reaction_id`](/rest/reference/reactions#delete-team-discussion-reaction) (:write)
- [`DELETE /orgs/:org/teams/:team_slug/discussions/:discussion_number/comments/:comment_number/reactions/:reaction_id`](/rest/reference/reactions#delete-team-discussion-comment-reaction) (:write){% endif %}
- [`DELETE /orgs/:org/teams/:team_slug/discussions/:discussion_number/comments/:comment_number/reactions/:reaction_id`](/rest/reference/reactions#delete-team-discussion-comment-reaction) (:write)
{% endif %}
_Releases_
- [`GET /repos/:owner/:repo/releases`](/rest/reference/repos/#list-releases) (:read)
@@ -425,8 +430,10 @@ _Releases_
- [`GET /repos/:owner/:repo/deployments`](/rest/reference/repos#list-deployments) (:read)
- [`POST /repos/:owner/:repo/deployments`](/rest/reference/repos#create-a-deployment) (:write)
- [`GET /repos/:owner/:repo/deployments/:deployment_id`](/rest/reference/repos#get-a-deployment) (:read){% if currentVersion == "free-pro-team@latest" or currentVersion ver_gt "enterprise-server@2.20" or currentVersion == "github-ae@latest" %}
- [`DELETE /repos/:owner/:repo/deployments/:deployment_id`](/rest/reference/repos#delete-a-deployment) (:write){% endif %}
- [`GET /repos/:owner/:repo/deployments/:deployment_id`](/rest/reference/repos#get-a-deployment) (:read)
{% if currentVersion == "free-pro-team@latest" or currentVersion ver_gt "enterprise-server@2.20" or currentVersion == "github-ae@latest" -%}
- [`DELETE /repos/:owner/:repo/deployments/:deployment_id`](/rest/reference/repos#delete-a-deployment) (:write)
{% endif -%}
- [`GET /repos/:owner/:repo/deployments/:deployment_id/statuses`](/rest/reference/repos#list-deployment-statuses) (:read)
- [`POST /repos/:owner/:repo/deployments/:deployment_id/statuses`](/rest/reference/repos#create-a-deployment-status) (:write)
- [`GET /repos/:owner/:repo/deployments/:deployment_id/statuses/:status_id`](/rest/reference/repos#get-a-deployment-status) (:read)
@@ -434,9 +441,9 @@ _Releases_
{% if currentVersion == "free-pro-team@latest" or enterpriseServerVersions contains currentVersion %}
### Permission on "emails"
{% if currentVersion == "free-pro-team@latest" %}
{% if currentVersion == "free-pro-team@latest" -%}
- [`PATCH /user/email/visibility`](/rest/reference/users#set-primary-email-visibility-for-the-authenticated-user) (:write)
{% endif %}
{% endif -%}
- [`GET /user/emails`](/rest/reference/users#list-email-addresses-for-the-authenticated-user) (:read)
- [`POST /user/emails`](/rest/reference/users#add-an-email-address-for-the-authenticated-user) (:write)
- [`DELETE /user/emails`](/rest/reference/users#delete-an-email-address-for-the-authenticated-user) (:write)
@@ -523,15 +530,17 @@ _Reactions_
- [`POST /repos/:owner/:repo/issues/comments/:comment_id/reactions`](/rest/reference/reactions#create-reaction-for-an-issue-comment) (:write)
- [`GET /repos/:owner/:repo/issues/:issue_number/reactions`](/rest/reference/reactions#list-reactions-for-an-issue) (:read)
- [`POST /repos/:owner/:repo/issues/:issue_number/reactions`](/rest/reference/reactions#create-reaction-for-an-issue) (:write)
{% if currentVersion == "free-pro-team@latest" or currentVersion ver_gt "enterprise-server@2.20" or currentVersion == "github-ae@latest" %}
{% if currentVersion == "free-pro-team@latest" or currentVersion ver_gt "enterprise-server@2.20" or currentVersion == "github-ae@latest" -%}
- [`DELETE /reactions/:reaction_id`](/rest/reference/reactions#delete-a-reaction-legacy) (:write)
- [`DELETE /repos/:owner/:repo/comments/:comment_id/reactions/:reaction_id`](/rest/reference/reactions#delete-a-commit-comment-reaction) (:write)
- [`DELETE /repos/:owner/:repo/issues/:issue_number/reactions/:reaction_id`](/rest/reference/reactions#delete-an-issue-reaction) (:write)
- [`DELETE /repos/:owner/:repo/issues/comments/:comment_id/reactions/:reaction_id`](/rest/reference/reactions#delete-an-issue-comment-reaction) (:write)
- [`DELETE /repos/:owner/:repo/pulls/comments/:comment_id/reactions/:reaction_id`](/rest/reference/reactions#delete-a-pull-request-comment-reaction) (:write)
- [`DELETE /orgs/:org/teams/:team_slug/discussions/:discussion_number/reactions/:reaction_id`](/rest/reference/reactions#delete-team-discussion-reaction) (:write)
- [`DELETE /orgs/:org/teams/:team_slug/discussions/:discussion_number/comments/:comment_number/reactions/:reaction_id`](/rest/reference/reactions#delete-team-discussion-comment-reaction) (:write){% else %}
- [`DELETE /reactions/:reaction_id`](/rest/reference/reactions#delete-a-reaction) (:write){% endif %}
- [`DELETE /orgs/:org/teams/:team_slug/discussions/:discussion_number/comments/:comment_number/reactions/:reaction_id`](/rest/reference/reactions#delete-team-discussion-comment-reaction) (:write)
{% else -%}
- [`DELETE /reactions/:reaction_id`](/rest/reference/reactions#delete-a-reaction) (:write)
{% endif %}
### Permission on "keys"
@@ -543,35 +552,35 @@ _Keys_
### Permission on "members"
{% if currentVersion == "free-pro-team@latest" %}
{% if currentVersion == "free-pro-team@latest" -%}
- [`GET /organizations/:org_id/team/:team_id/team-sync/group-mappings`](/rest/reference/teams#list-idp-groups-for-a-team) (:write)
{% endif %}
{% if currentVersion == "free-pro-team@latest" %}
{% endif -%}
{% if currentVersion == "free-pro-team@latest" -%}
- [`PATCH /organizations/:org_id/team/:team_id/team-sync/group-mappings`](/rest/reference/teams#create-or-update-idp-group-connections) (:write)
{% endif %}
{% endif -%}
- [`GET /orgs/:org/outside_collaborators`](/rest/reference/orgs#list-outside-collaborators-for-an-organization) (:read)
- [`PUT /orgs/:org/outside_collaborators/:username`](/rest/reference/orgs#convert-an-organization-member-to-outside-collaborator) (:write)
- [`DELETE /orgs/:org/outside_collaborators/:username`](/rest/reference/orgs#remove-outside-collaborator-from-an-organization) (:write)
{% if currentVersion == "free-pro-team@latest" %}
{% if currentVersion == "free-pro-team@latest" -%}
- [`GET /orgs/:org/team-sync/groups`](/rest/reference/teams#list-idp-groups-for-an-organization) (:write)
{% endif %}
{% endif -%}
- [`GET /orgs/:org/team/:team_id`](/rest/reference/teams#get-a-team-by-name) (:read)
{% if currentVersion == "free-pro-team@latest" %}
{% if currentVersion == "free-pro-team@latest" -%}
- [`GET /scim/v2/orgs/:org/Users`](/rest/reference/scim#list-scim-provisioned-identities) (:read)
{% endif %}
{% if currentVersion == "free-pro-team@latest" %}
{% endif -%}
{% if currentVersion == "free-pro-team@latest" -%}
- [`POST /scim/v2/orgs/:org/Users`](/rest/reference/scim#provision-and-invite-a-scim-user) (:write)
{% endif %}
{% if currentVersion == "free-pro-team@latest" %}
{% endif -%}
{% if currentVersion == "free-pro-team@latest" -%}
- [`GET /scim/v2/orgs/:org/Users/:external_identity_guid`](/rest/reference/scim#get-scim-provisioning-information-for-a-user) (:read)
{% endif %}
{% if currentVersion == "free-pro-team@latest" %}
{% endif -%}
{% if currentVersion == "free-pro-team@latest" -%}
- [`PUT /scim/v2/orgs/:org/Users/:external_identity_guid`](/rest/reference/scim#set-scim-information-for-a-provisioned-user) (:write)
{% endif %}
{% if currentVersion == "free-pro-team@latest" %}
{% endif -%}
{% if currentVersion == "free-pro-team@latest" -%}
- [`PATCH /scim/v2/orgs/:org/Users/:external_identity_guid`](/rest/reference/scim#update-an-attribute-for-a-scim-user) (:write)
{% endif %}
{% if currentVersion == "free-pro-team@latest" %}
{% endif -%}
{% if currentVersion == "free-pro-team@latest" -%}
- [`DELETE /scim/v2/orgs/:org/Users/:external_identity_guid`](/rest/reference/scim#delete-a-scim-user-from-an-organization) (:write)
{% endif %}
@@ -606,12 +615,12 @@ _Teams_
- [`GET /orgs/:org/teams/:team_slug`](/rest/reference/teams#get-a-team-by-name) (:read)
- [`PATCH /teams/:team_id`](/rest/reference/teams#update-a-team) (:write)
- [`DELETE /teams/:team_id`](/rest/reference/teams#delete-a-team) (:write)
{% if currentVersion == "free-pro-team@latest" or currentVersion ver_gt "enterprise-server@2.20" or currentVersion == "github-ae@latest" %}
{% if currentVersion == "free-pro-team@latest" or currentVersion ver_gt "enterprise-server@2.20" or currentVersion == "github-ae@latest" -%}
- [`GET /teams/:team_id/projects`](/rest/reference/teams#list-team-projects) (:read)
- [`GET /teams/:team_id/projects/:project_id`](/rest/reference/teams#check-team-permissions-for-a-project) (:read)
- [`PUT /teams/:team_id/projects/:project_id`](/rest/reference/teams#add-or-update-team-project-permissions) (:read)
- [`DELETE /teams/:team_id/projects/:project_id`](/rest/reference/teams#remove-a-project-from-a-team) (:read)
{% endif %}
{% endif -%}
- [`GET /teams/:team_id/repos`](/rest/reference/teams#list-team-repositories) (:read)
- [`GET /teams/:team_id/repos/:owner/:repo`](/rest/reference/teams#check-team-permissions-for-a-repository) (:read)
- [`PUT /teams/:team_id/repos/:owner/:repo`](/rest/reference/teams#add-or-update-team-repository-permissions) (:read)
@@ -621,13 +630,13 @@ _Teams_
### Permission on "organization administration"
- [`PATCH /orgs/:org`](/rest/reference/orgs#update-an-organization) (:write)
{% if currentVersion == "free-pro-team@latest" %}
{% if currentVersion == "free-pro-team@latest" -%}
- [`GET /orgs/:org/interaction-limits`](/rest/reference/interactions#get-interaction-restrictions-for-an-organization) (:read)
{% endif %}
{% if currentVersion == "free-pro-team@latest" %}
{% endif -%}
{% if currentVersion == "free-pro-team@latest" -%}
- [`PUT /orgs/:org/interaction-limits`](/rest/reference/interactions#set-interaction-restrictions-for-an-organization) (:write)
{% endif %}
{% if currentVersion == "free-pro-team@latest" %}
{% endif -%}
{% if currentVersion == "free-pro-team@latest" -%}
- [`DELETE /orgs/:org/interaction-limits`](/rest/reference/interactions#remove-interaction-restrictions-for-an-organization) (:write)
{% endif %}
@@ -695,7 +704,7 @@ _Teams_
- [`POST /repos/:owner/:repo/pages/builds`](/rest/reference/repos#request-a-github-pages-build) (:write)
- [`GET /repos/:owner/:repo/pages/builds/:build_id`](/rest/reference/repos#get-github-pages-build) (:read)
- [`GET /repos/:owner/:repo/pages/builds/latest`](/rest/reference/repos#get-latest-pages-build) (:read)
{% if currentVersion == "free-pro-team@latest" %}
{% if currentVersion == "free-pro-team@latest" -%}
- [`GET /repos/:owner/:repo/pages/health`](/rest/reference/repos#get-a-dns-health-check-for-github-pages) (:write)
{% endif %}
@@ -764,14 +773,17 @@ _Reactions_
- [`POST /repos/:owner/:repo/issues/comments/:comment_id/reactions`](/rest/reference/reactions#create-reaction-for-an-issue-comment) (:write)
- [`GET /repos/:owner/:repo/pulls/comments/:comment_id/reactions`](/rest/reference/reactions#list-reactions-for-a-pull-request-review-comment) (:read)
- [`POST /repos/:owner/:repo/pulls/comments/:comment_id/reactions`](/rest/reference/reactions#create-reaction-for-a-pull-request-review-comment) (:write)
{% if currentVersion == "free-pro-team@latest" or currentVersion ver_gt "enterprise-server@2.20" or currentVersion == "github-ae@latest" %}
{% if currentVersion == "free-pro-team@latest" or currentVersion ver_gt "enterprise-server@2.20" or currentVersion == "github-ae@latest" -%}
- [`DELETE /reactions/:reaction_id`](/rest/reference/reactions#delete-a-reaction-legacy) (:write)
- [`DELETE /repos/:owner/:repo/comments/:comment_id/reactions/:reaction_id`](/rest/reference/reactions#delete-a-commit-comment-reaction) (:write)
- [`DELETE /repos/:owner/:repo/issues/:issue_number/reactions/:reaction_id`](/rest/reference/reactions#delete-an-issue-reaction) (:write)
- [`DELETE /repos/:owner/:repo/issues/comments/:comment_id/reactions/:reaction_id`](/rest/reference/reactions#delete-an-issue-comment-reaction) (:write)
- [`DELETE /repos/:owner/:repo/pulls/comments/:comment_id/reactions/:reaction_id`](/rest/reference/reactions#delete-a-pull-request-comment-reaction) (:write)
- [`DELETE /orgs/:org/teams/:team_slug/discussions/:discussion_number/reactions/:reaction_id`](/rest/reference/reactions#delete-team-discussion-reaction) (:write)
- [`DELETE /orgs/:org/teams/:team_slug/discussions/:discussion_number/comments/:comment_number/reactions/:reaction_id`](/rest/reference/reactions#delete-team-discussion-comment-reaction) (:write){% else %}- [`DELETE /reactions/:reaction_id`](/rest/reference/reactions#delete-a-reaction) (:write){% endif %}
- [`DELETE /orgs/:org/teams/:team_slug/discussions/:discussion_number/comments/:comment_number/reactions/:reaction_id`](/rest/reference/reactions#delete-team-discussion-comment-reaction) (:write)
{% else -%}
- [`DELETE /reactions/:reaction_id`](/rest/reference/reactions#delete-a-reaction) (:write)
{% endif %}
_Requested reviewers_
- [`GET /repos/:owner/:repo/pulls/:pull_number/requested_reviewers`](/rest/reference/pulls#list-requested-reviewers-for-a-pull-request) (:read)
@@ -837,29 +849,27 @@ _Teams_
{% if currentVersion == "free-pro-team@latest" %}
### Permission on "secrets"
* [`GET /repos/:owner/:repo/actions/secrets/public-key`](/rest/reference/actions#get-a-repository-public-key) (:read)
* [`GET /repos/:owner/:repo/actions/secrets`](/rest/reference/actions#list-repository-secrets) (:read)
* [`GET /repos/:owner/:repo/actions/secrets/:secret_name`](/rest/reference/actions#get-a-repository-secret) (:read)
* [`PUT /repos/:owner/:repo/actions/secrets/:secret_name`](/rest/reference/actions#create-or-update-a-repository-secret) (:write)
* [`DELETE /repos/:owner/:repo/actions/secrets/:secret_name`](/rest/reference/actions#delete-a-repository-secret) (:write)
* [`GET /orgs/:org/actions/secrets/public-key`](/rest/reference/actions#get-an-organization-public-key) (:read)
* [`GET /orgs/:org/actions/secrets`](/rest/reference/actions#list-organization-secrets) (:read)
* [`GET /orgs/:org/actions/secrets/:secret_name`](/rest/reference/actions#get-an-organization-secret) (:read)
* [`PUT /orgs/:org/actions/secrets/:secret_name`](/rest/reference/actions#create-or-update-an-organization-secret) (:write)
* [`GET /orgs/:org/actions/secrets/:secret_name/repositories`](/rest/reference/actions#list-selected-repositories-for-an-organization-secret) (:read)
* [`PUT /orgs/:org/actions/secrets/:secret_name/repositories`](/rest/reference/actions#set-selected-repositories-for-an-organization-secret) (:write)
* [`PUT /orgs/:org/actions/secrets/:secret_name/repositories/:repository_id`](/rest/reference/actions#add-selected-repository-to-an-organization-secret) (:write)
* [`DELETE /orgs/:org/actions/secrets/:secret_name/repositories/:repository_id`](/rest/reference/actions#remove-selected-repository-from-an-organization-secret) (:write)
* [`DELETE /orgs/:org/actions/secrets/:secret_name`](/rest/reference/actions#delete-an-organization-secret) (:write)
- [`GET /repos/:owner/:repo/actions/secrets/public-key`](/rest/reference/actions#get-a-repository-public-key) (:read)
- [`GET /repos/:owner/:repo/actions/secrets`](/rest/reference/actions#list-repository-secrets) (:read)
- [`GET /repos/:owner/:repo/actions/secrets/:secret_name`](/rest/reference/actions#get-a-repository-secret) (:read)
- [`PUT /repos/:owner/:repo/actions/secrets/:secret_name`](/rest/reference/actions#create-or-update-a-repository-secret) (:write)
- [`DELETE /repos/:owner/:repo/actions/secrets/:secret_name`](/rest/reference/actions#delete-a-repository-secret) (:write)
- [`GET /orgs/:org/actions/secrets/public-key`](/rest/reference/actions#get-an-organization-public-key) (:read)
- [`GET /orgs/:org/actions/secrets`](/rest/reference/actions#list-organization-secrets) (:read)
- [`GET /orgs/:org/actions/secrets/:secret_name`](/rest/reference/actions#get-an-organization-secret) (:read)
- [`PUT /orgs/:org/actions/secrets/:secret_name`](/rest/reference/actions#create-or-update-an-organization-secret) (:write)
- [`GET /orgs/:org/actions/secrets/:secret_name/repositories`](/rest/reference/actions#list-selected-repositories-for-an-organization-secret) (:read)
- [`PUT /orgs/:org/actions/secrets/:secret_name/repositories`](/rest/reference/actions#set-selected-repositories-for-an-organization-secret) (:write)
- [`PUT /orgs/:org/actions/secrets/:secret_name/repositories/:repository_id`](/rest/reference/actions#add-selected-repository-to-an-organization-secret) (:write)
- [`DELETE /orgs/:org/actions/secrets/:secret_name/repositories/:repository_id`](/rest/reference/actions#remove-selected-repository-from-an-organization-secret) (:write)
- [`DELETE /orgs/:org/actions/secrets/:secret_name`](/rest/reference/actions#delete-an-organization-secret) (:write)
{% endif %}
{% if currentVersion == "free-pro-team@latest" or currentVersion ver_gt "enterprise-server@3.0" %}
### Permission on "secret scanning alerts"
- [`GET /repos/:owner/:repo/secret-scanning/alerts`](/rest/reference/secret-scanning#list-secret-scanning-alerts-for-a-repository) (:read)
- [`GET /repos/:owner/:repo/secret-scanning/alerts/:alert_number`](/rest/reference/secret-scanning#get-a-secret-scanning-alert) (:read)
- [`PATCH /repos/:owner/:repo/secret-scanning/alerts/:alert_number`](/rest/reference/secret-scanning#update-a-secret-scanning-alert) (:write)
{% endif %}
@@ -869,24 +879,20 @@ _Teams_
- [`GET /repos/:owner/:repo/code-scanning/alerts`](/rest/reference/code-scanning#list-code-scanning-alerts-for-a-repository) (:read)
- [`GET /repos/:owner/:repo/code-scanning/alerts/:alert_number`](/rest/reference/code-scanning#get-a-code-scanning-alert) (:read)
- [`PATCH /repos/:owner/:repo/code-scanning/alerts/:alert_number`](/rest/reference/code-scanning#update-a-code-scanning-alert) (:write)
{% endif %}
{% if currentVersion == "free-pro-team@latest" or currentVersion ver_gt "enterprise-server@3.0" or currentVersion == "github-ae@latest" %}
{% if currentVersion == "free-pro-team@latest" or currentVersion ver_gt "enterprise-server@3.0" or currentVersion == "github-ae@latest" -%}
- [`GET /repos/:owner/:repo/code-scanning/alerts/:alert_number/instances`](/rest/reference/code-scanning#list-instances-of-a-code-scanning-alert) (:read)
{% endif %}
{% if currentVersion == "free-pro-team@latest" or currentVersion ver_gt "enterprise-server@2.21" or currentVersion == "github-ae@latest" %}
{% endif -%}
- [`GET /repos/:owner/:repo/code-scanning/analyses`](/rest/reference/code-scanning#list-code-scanning-analyses-for-a-repository) (:read)
{% endif %}
{% if currentVersion == "free-pro-team@latest" or currentVersion ver_gt "enterprise-server@3.0" or currentVersion == "github-ae@latest" %}
{% if currentVersion == "free-pro-team@latest" or currentVersion ver_gt "enterprise-server@3.0" or currentVersion == "github-ae@latest" -%}
- [`GET /repos/:owner/:repo/code-scanning/analyses/:analysis_id`](/rest/reference/code-scanning#get-a-code-scanning-analysis-for-a-repository) (:read)
{% endif %}
{% if currentVersion == "free-pro-team@latest" or currentVersion ver_gt "enterprise-server@3.0" %}
{% endif -%}
{% if currentVersion == "free-pro-team@latest" or currentVersion ver_gt "enterprise-server@3.0" -%}
- [`DELETE /repos/:owner/:repo/code-scanning/analyses/:analysis_id`](/rest/reference/code-scanning#delete-a-code-scanning-analysis-from-a-repository) (:write)
{% endif %}
{% if currentVersion == "free-pro-team@latest" or currentVersion ver_gt "enterprise-server@2.21" or currentVersion == "github-ae@latest" %}
{% endif -%}
- [`POST /repos/:owner/:repo/code-scanning/sarifs`](/rest/reference/code-scanning#upload-an-analysis-as-sarif-data) (:write)
{% endif %}
{% if currentVersion == "free-pro-team@latest" or currentVersion ver_gt "enterprise-server@3.0" or currentVersion == "github-ae@latest" %}
{% if currentVersion == "free-pro-team@latest" or currentVersion ver_gt "enterprise-server@3.0" or currentVersion == "github-ae@latest" -%}
- [`GET /repos/:owner/:repo/code-scanning/sarifs/:sarif_id`](/rest/reference/code-scanning#get-information-about-a-sarif-upload) (:read)
{% endif -%}
{% endif %}
{% if currentVersion == "free-pro-team@latest" %}

52
data/features/README.md Normal file
View File

@@ -0,0 +1,52 @@
## Feature-based versioning
Feature-based versioning allows us to define and control the versions of an arbitrarily named "feature" in one place.
**Note**: Do not delete `data/features/placeholder.yml` because it is used by tests.
## How it works
Add a new YAML file with the feature name you want to use in this directory. For a feature named `meow`, that would be `data/features/meow.yml`.
Add a `versions` block to the YML file with the short names of the versions the feature is available in. For example:
```yaml
versions:
fpt: '*'
ghes: '>3.1'
ghae: '*'
```
The format and allowed values are the same as the [frontmatter versions property](/content#versions).
### Liquid conditionals
Now you can use `{% if meow %} ... {% endif %}` in content files! Note this is the `if` tag, not the new `ifversion` tag.
### Frontmatter
You can also use the feature in frontmatter in content files:
```yaml
versions:
fpt: '*'
ghes: '>3.1'
feature: 'meow'
```
If you want a content file to apply to more than one feature, you can do this:
```yaml
versions:
fpt: '*'
ghes: '>3.1'
feature: ['meow', 'blorp']
```
## Schema enforcement
The schema for validating the feature versioning lives in [`tests/helpers/schemas/feature-versions.js`](tests/helpers/schemas/feature-versions.js) and is exercised by [`tests/content/lint-files.js`](tests/content/lint-files.js).
## Script to remove feature tags
TBD!

View File

@@ -0,0 +1,4 @@
# Do not delete! Used by tests.
versions:
ghes: '>3.0'
ghae: '*'

View File

@@ -1,7 +0,0 @@
{% if currentVersion == "free-pro-team@latest" or currentVersion ver_gt "enterprise-server@3.0" or currentVersion == "github-ae@latest" %}
{% note %}
**Note:** The workflow visualization graph for {% data variables.product.prodname_actions %} is currently in beta and subject to change.
{% endnote %}
{% endif %}

View File

@@ -4,6 +4,6 @@ For example, on the command line you would enter the following:
```shell
$ git clone https://{% data variables.command_line.codeblock %}/<em>username</em>/<em>repo</em>.git
Username: <code>your_username</code>
Username: <em>your_username</em>
Password: <em>your_token</em>
```

View File

@@ -1,5 +1,10 @@
const express = require('express')
const middleware = require('../middleware')
const app = express()
require('../middleware')(app)
module.exports = app
function createApp () {
const app = express()
middleware(app)
return app
}
module.exports = createApp

View File

@@ -1,4 +1,5 @@
const featureFlags = require('../feature-flags')
const readJsonFile = require('./read-json-file')
const featureFlags = readJsonFile('./feature-flags.json')
// add feature flags as environment variables
Object.entries(featureFlags).forEach(([feature, value]) => {

View File

@@ -1,3 +1,5 @@
const fs = require('fs')
const path = require('path')
const parse = require('./read-frontmatter')
const semver = require('semver')
const layouts = require('./layouts')
@@ -9,8 +11,10 @@ const semverRange = {
conform: semverValidRange,
message: 'Must be a valid SemVer range'
}
const versionIds = Object.keys(require('./all-versions'))
const versionObjs = Object.values(require('./all-versions'))
const guideTypes = ['overview', 'quick_start', 'tutorial', 'how_to', 'reference']
const featureVersions = fs.readdirSync(path.posix.join(process.cwd(), 'data/features'))
.map(file => path.basename(file, '.yml'))
const schema = {
properties: {
@@ -197,15 +201,31 @@ const schema = {
}
}
const featureVersionsProp = {
feature: {
type: ['string', 'array'],
enum: featureVersions,
items: {
type: 'string'
},
message: 'must be the name (or names) of a feature that matches "filename" in data/features/_filename_.yml'
}
}
schema.properties.versions = {
type: ['object', 'string'], // allow a '*' string to indicate all versions
required: true,
properties: versionIds.reduce((acc, versionId) => {
acc[versionId] = semverRange
properties: versionObjs.reduce((acc, versionObj) => {
acc[versionObj.plan] = semverRange
acc[versionObj.shortName] = semverRange
return acc
}, {})
}, featureVersionsProp)
}
// Support 'github-ae': next
schema.properties.versions.properties['github-ae'] = 'next'
schema.properties.versions.properties.ghae = 'next'
function frontmatter (markdown, opts = {}) {
const defaults = {
schema,

View File

@@ -1,6 +1,17 @@
const path = require('path')
const { reduce, sortBy } = require('lodash')
const allVersions = require('./all-versions')
const versionSatisfiesRange = require('./version-satisfies-range')
const checkIfNextVersionOnly = require('./check-if-next-version-only')
const dataDirectory = require('./data-directory')
const encodeBracketedParentheses = require('./encode-bracketed-parentheses')
const featuresDir = path.posix.join(__dirname, '../data/features')
const featureData = dataDirectory(featuresDir, {
preprocess: dataString =>
encodeBracketedParentheses(dataString.trimEnd()),
ignorePatterns: [/README\.md$/]
})
// return an array of versions that an article's product versions encompasses
function getApplicableVersions (frontmatterVersions, filepath) {
@@ -13,17 +24,63 @@ function getApplicableVersions (frontmatterVersions, filepath) {
return Object.keys(allVersions)
}
// get an array like: [ 'free-pro-team@latest', 'enterprise-server@2.21', 'enterprise-cloud@latest' ]
const applicableVersions = []
// Check for frontmatter that includes a feature name, like:
// fpt: '*'
// feature: 'foo'
// or multiple feature names, like:
// fpt: '*'
// feature: ['foo', 'bar']
// and add the versions affiliated with the feature (e.g., foo) to the frontmatter versions object:
// fpt: '*'
// ghes: '>=2.23'
// ghae: '*'
// where the feature is bringing the ghes and ghae versions into the mix.
const featureVersions = reduce(frontmatterVersions, (result, value, key) => {
if (key === 'feature') {
if (typeof value === 'string') {
Object.assign(result, { ...featureData[value].versions })
} else if (Array.isArray(value)) {
value.forEach(str => {
Object.assign(result, { ...featureData[str].versions })
})
}
delete result[key]
}
return result
}, {})
// We will be evaluating feature versions separately, so we can remove this.
delete frontmatterVersions.feature
// Get available versions for frontmatter and for feature versions.
const foundFeatureVersions = evaluateVersions(featureVersions)
const foundFrontmatterVersions = evaluateVersions(frontmatterVersions)
// Combine them!
const applicableVersions = [...new Set(foundFrontmatterVersions.versions.concat(foundFeatureVersions.versions))]
if (!applicableVersions.length && !foundFrontmatterVersions.isNextVersionOnly && !foundFeatureVersions.isNextVersionOnly) {
throw new Error(`No applicable versions found for ${filepath}. Please double-check the page's \`versions\` frontmatter.`)
}
// Sort them by the order in lib/all-versions.
const sortedVersions = sortBy(applicableVersions, (v) => { return Object.keys(allVersions).indexOf(v) })
return sortedVersions
}
function evaluateVersions (versionsObj) {
let isNextVersionOnly = false
// where frontmatter is something like:
// get an array like: [ 'free-pro-team@latest', 'enterprise-server@2.21', 'enterprise-cloud@latest' ]
const versions = []
// where versions obj is something like:
// fpt: '*'
// ghes: '>=2.19'
// ghae: '*'
// ^ where each key corresponds to a plan's short name (defined in lib/all-versions.js)
Object.entries(frontmatterVersions)
Object.entries(versionsObj)
.forEach(([plan, planValue]) => {
// Special handling for frontmatter that evalues to the next GHES release number or a hardcoded `next`.
isNextVersionOnly = checkIfNextVersionOnly(planValue)
@@ -37,16 +94,12 @@ function getApplicableVersions (frontmatterVersions, filepath) {
const versionToCompare = relevantVersion.hasNumberedReleases ? relevantVersion.currentRelease : '1.0'
if (versionSatisfiesRange(versionToCompare, planValue)) {
applicableVersions.push(relevantVersion.version)
versions.push(relevantVersion.version)
}
})
})
if (!applicableVersions.length && !isNextVersionOnly) {
throw new Error(`No applicable versions found for ${filepath}. Please double-check the page's \`versions\` frontmatter.`)
}
return applicableVersions
return { versions, isNextVersionOnly }
}
module.exports = getApplicableVersions

View File

@@ -1,11 +1,10 @@
const path = require('path')
const statsd = require('./statsd')
module.exports = function instrumentMiddleware (relativePath) {
module.exports = function instrumentMiddleware (middleware, relativePath) {
// Requires the file as if it were being required from '../middleware/index.js'.
// This is a little wonky, but let's us write `app.use(instrument(path))` and
// maintain the name of the file, instead of hard-coding it for each middleware.
const middleware = require(path.resolve(__dirname, '../middleware', relativePath))
// Check if the middleware is an async function, to use the appropriate timer
const isAsyncFunction = middleware.constructor.name === 'AsyncFunction'

14
lib/read-json-file.js Normal file
View File

@@ -0,0 +1,14 @@
const fs = require('fs')
const path = require('path')
module.exports = function readJsonFile (xpath) {
return JSON.parse(
fs.readFileSync(
path.join(
process.cwd(),
xpath
),
'utf8'
)
)
}

View File

@@ -1,4 +1,5 @@
const developerRedirects = require('../redirects/static/developer')
const readJsonFile = require('../read-json-file')
const developerRedirects = readJsonFile('./lib/redirects/static/developer.json')
const { latest } = require('../../lib/enterprise-server-releases')
const latestDevRedirects = {}

View File

@@ -1,15 +1,17 @@
const path = require('path')
const visit = require('unist-util-visit')
const externalRedirects = Object.keys(require('../../redirects/external-sites'))
const { getPathWithoutLanguage, getVersionStringFromPath } = require('../../path-utils')
const { getNewVersionedPath } = require('../../old-versions-utils')
const patterns = require('../../patterns')
const { deprecated, latest } = require('../../enterprise-server-releases')
const nonEnterpriseDefaultVersion = require('../../non-enterprise-default-version')
const allVersions = require('../../all-versions')
const removeFPTFromPath = require('../../remove-fpt-from-path')
const supportedVersions = Object.keys(allVersions)
const supportedPlans = Object.values(allVersions).map(v => v.plan)
const removeFPTFromPath = require('../../remove-fpt-from-path')
const readJsonFile = require('../../read-json-file')
const externalRedirects = Object.keys(readJsonFile('./lib/redirects/external-sites.json'))
// Matches any <a> tags with an href that starts with `/`
const matcher = node => (

File diff suppressed because one or more lines are too long

View File

@@ -1317,12 +1317,6 @@
}
}
},
"/app/hook/deliveries": {
},
"/app/hook/deliveries/{delivery_id}": {
},
"/app/hook/deliveries/{delivery_id}/attempts": {
},
"/app/installations": {
"get": {
"summary": "List installations for the authenticated app",
@@ -68643,12 +68637,6 @@
}
}
},
"/orgs/{org}/hooks/{hook_id}/deliveries": {
},
"/orgs/{org}/hooks/{hook_id}/deliveries/{delivery_id}": {
},
"/orgs/{org}/hooks/{hook_id}/deliveries/{delivery_id}/attempts": {
},
"/orgs/{org}/hooks/{hook_id}/pings": {
"post": {
"summary": "Ping an organization webhook",
@@ -91252,7 +91240,7 @@
"/orgs/{org}/settings/billing/packages": {
"get": {
"summary": "Get GitHub Packages billing for an organization",
"description": "Gets the free and paid storage usued for GitHub Packages in gigabytes.\n\nPaid minutes only apply to packages stored for private repositories. For more information, see \"[Managing billing for GitHub Packages](https://help.github.com/github/setting-up-and-managing-billing-and-payments-on-github/managing-billing-for-github-packages).\"\n\nAccess tokens must have the `repo` or `admin:org` scope.",
"description": "Gets the free and paid storage used for GitHub Packages in gigabytes.\n\nPaid minutes only apply to packages stored for private repositories. For more information, see \"[Managing billing for GitHub Packages](https://help.github.com/github/setting-up-and-managing-billing-and-payments-on-github/managing-billing-for-github-packages).\"\n\nAccess tokens must have the `repo` or `admin:org` scope.",
"operationId": "billing/get-github-packages-billing-org",
"tags": [
"billing"
@@ -116299,6 +116287,23 @@
"internal"
]
},
"security_and_analysis": {
"type": "object",
"description": "Specify which security and analysis features to enable or disable.",
"nullable": true,
"properties": {
"advanced_security": {
"type": "object",
"description": "Use the `status` property to enable or disable GitHub Advanced Security for this repository. For more information, see \"[About GitHub Advanced Security](/github/getting-started-with-github/learning-about-github/about-github-advanced-security).\" If you're an admin user for a repository covered by an Advanced Security license, you can check whether Advanced Security is currently enabled by using a `GET /repos/{owner}/{repo}` request.",
"properties": {
"status": {
"type": "string",
"description": "Can be `enabled` or `disabled`."
}
}
}
}
},
"has_issues": {
"type": "boolean",
"description": "Either `true` to enable issues for this repository or `false` to disable them.",
@@ -216881,12 +216886,6 @@
}
}
},
"/repos/{owner}/{repo}/hooks/{hook_id}/deliveries": {
},
"/repos/{owner}/{repo}/hooks/{hook_id}/deliveries/{delivery_id}": {
},
"/repos/{owner}/{repo}/hooks/{hook_id}/deliveries/{delivery_id}/attempts": {
},
"/repos/{owner}/{repo}/hooks/{hook_id}/pings": {
"post": {
"summary": "Ping a repository webhook",
@@ -323399,8 +323398,6 @@
}
}
},
"/repos/{owner}/{repo}/releases/{release_id}/reactions/{reaction_id}": {
},
"/repos/{owner}/{repo}/secret-scanning/alerts": {
"get": {
"summary": "List secret scanning alerts for a repository",
@@ -363681,13 +363678,8 @@
"application/json": {
"schema": {
"properties": {
"email": {
"description": "An email address associated with the GitHub user account to manage.",
"type": "string",
"example": "org@example.com"
},
"visibility": {
"description": "Denotes whether an email is publically visible.",
"description": "Denotes whether an email is publicly visible.",
"type": "string",
"enum": [
"public",
@@ -397436,8 +397428,6 @@
}
}
},
"/user/tokens/reset": {
},
"/users": {
"get": {
"summary": "List users",

View File

@@ -4986,12 +4986,6 @@
}
}
},
"/app/hook/deliveries": {
},
"/app/hook/deliveries/{delivery_id}": {
},
"/app/hook/deliveries/{delivery_id}/attempts": {
},
"/app/installations": {
"get": {
"summary": "List installations for the authenticated app",
@@ -43531,10 +43525,6 @@
}
}
},
"/orgs/{org}/hooks/{hook_id}/deliveries/{delivery_id}": {
},
"/orgs/{org}/hooks/{hook_id}/deliveries/{delivery_id}/attempts": {
},
"/orgs/{org}/hooks/{hook_id}/pings": {
"post": {
"summary": "Ping an organization webhook",
@@ -170265,12 +170255,6 @@
}
}
},
"/repos/{owner}/{repo}/hooks/{hook_id}/deliveries": {
},
"/repos/{owner}/{repo}/hooks/{hook_id}/deliveries/{delivery_id}": {
},
"/repos/{owner}/{repo}/hooks/{hook_id}/deliveries/{delivery_id}/attempts": {
},
"/repos/{owner}/{repo}/hooks/{hook_id}/pings": {
"post": {
"summary": "Ping a repository webhook",
@@ -274396,8 +274380,6 @@
}
}
},
"/repos/{owner}/{repo}/releases/{release_id}/reactions/{reaction_id}": {
},
"/repos/{owner}/{repo}/stargazers": {
"get": {
"summary": "List stargazers",
@@ -326059,8 +326041,6 @@
}
}
},
"/user/tokens/reset": {
},
"/users": {
"get": {
"summary": "List users",

View File

@@ -1,6 +1,5 @@
const assert = require('assert')
const path = require('path')
const externalRedirects = Object.keys(require('./redirects/external-sites'))
const { getPathWithoutLanguage, getVersionStringFromPath } = require('./path-utils')
const { getNewVersionedPath } = require('./old-versions-utils')
const patterns = require('./patterns')
@@ -10,6 +9,8 @@ const allVersions = require('./all-versions')
const supportedVersions = Object.keys(allVersions)
const supportedPlans = Object.values(allVersions).map(v => v.plan)
const removeFPTFromPath = require('./remove-fpt-from-path')
const readJsonFile = require('./read-json-file')
const externalRedirects = readJsonFile('./lib/redirects/external-sites.json')
// Content authors write links like `/some/article/path`, but they need to be
// rewritten on the fly to match the current language and page version

View File

@@ -1,6 +1,6 @@
const algoliasearch = require('algoliasearch')
const { get } = require('lodash')
const { namePrefix } = require('./config')
const { namePrefix } = require('./config.js')
// https://www.algolia.com/apps/ZI5KPY1HBE/dashboard
// This API key is public. There's also a private API key for writing to the Algolia API

View File

@@ -8,7 +8,7 @@ require('lunr-languages/lunr.pt')(lunr)
require('lunr-languages/lunr.de')(lunr)
const { get } = require('lodash')
const readFileAsync = require('../readfile-async')
const { namePrefix } = require('./config')
const { namePrefix } = require('./config.js')
const { decompress } = require('./compress')
const LUNR_DIR = './indexes'

View File

@@ -5,8 +5,9 @@ const patterns = require('../lib/patterns')
const versionSatisfiesRange = require('../lib/version-satisfies-range')
const isArchivedVersion = require('../lib/is-archived-version')
const got = require('got')
const archvivedRedirects = require('../lib/redirects/static/archived-redirects-from-213-to-217')
const archivedFrontmatterFallbacks = require('../lib/redirects/static/archived-frontmatter-fallbacks')
const readJsonFile = require('../lib/read-json-file')
const archvivedRedirects = readJsonFile('./lib/redirects/static/archived-redirects-from-213-to-217.json')
const archivedFrontmatterFallbacks = readJsonFile('./lib/redirects/static/archived-frontmatter-fallbacks.json')
// This module handles requests for deprecated GitHub Enterprise versions
// by routing them to static content in help-docs-archived-enterprise-versions

View File

@@ -11,7 +11,8 @@ const {
} = require('../lib/path-utils')
const productNames = require('../lib/product-names')
const warmServer = require('../lib/warm-server')
const featureFlags = Object.keys(require('../feature-flags'))
const readJsonFile = require('../lib/read-json-file')
const featureFlags = Object.keys(readJsonFile('./feature-flags.json'))
const builtAssets = require('../lib/built-asset-urls')
const searchVersions = require('../lib/search/versions')
const nonEnterpriseDefaultVersion = require('../lib/non-enterprise-default-version')

View File

@@ -0,0 +1,18 @@
const getApplicableVersions = require('../../lib/get-applicable-versions')
module.exports = async function features (req, res, next) {
if (!req.context.page) return next()
// Determine whether the currentVersion belongs to the list of versions the feature is available in.
Object.keys(req.context.site.data.features).forEach(featureName => {
const { versions } = req.context.site.data.features[featureName]
const applicableVersions = getApplicableVersions(versions, req.path)
// Adding the resulting boolean to the context object gives us the ability to use
// `{% if featureName ... %}` conditionals in content files.
const isFeatureAvailableInCurrentVersion = applicableVersions.includes(req.context.currentVersion)
req.context[featureName] = isFeatureAvailableInCurrentVersion
})
return next()
}

View File

@@ -1,10 +1,11 @@
const fs = require('fs')
const path = require('path')
const previews = require('../../lib/graphql/static/previews')
const upcomingChanges = require('../../lib/graphql/static/upcoming-changes')
const changelog = require('../../lib/graphql/static/changelog')
const prerenderedObjects = require('../../lib/graphql/static/prerendered-objects')
const prerenderedInputObjects = require('../../lib/graphql/static/prerendered-input-objects')
const readJsonFile = require('../../lib/read-json-file')
const previews = readJsonFile('./lib/graphql/static/previews.json')
const upcomingChanges = readJsonFile('./lib/graphql/static/upcoming-changes.json')
const changelog = readJsonFile('./lib/graphql/static/changelog.json')
const prerenderedObjects = readJsonFile('./lib/graphql/static/prerendered-objects.json')
const prerenderedInputObjects = readJsonFile('./lib/graphql/static/prerendered-input-objects.json')
const allVersions = require('../../lib/all-versions')
const explorerUrl = process.env.NODE_ENV === 'production'

View File

@@ -1,6 +1,5 @@
const path = require('path')
const { defaults } = require('lodash')
const webhookPayloads = require(path.join(process.cwd(), 'lib/webhooks'))
const webhookPayloads = require('../../lib/webhooks')
const nonEnterpriseDefaultVersion = require('../../lib/non-enterprise-default-version')
const allVersions = require('../../lib/all-versions')

View File

@@ -21,6 +21,44 @@ const reqUtils = require('./req-utils')
const recordRedirect = require('./record-redirect')
const connectSlashes = require('connect-slashes')
const handleErrors = require('./handle-errors')
const handleInvalidPaths = require('./handle-invalid-paths')
const handleNextDataPath = require('./handle-next-data-path')
const detectLanguage = require('./detect-language')
const context = require('./context')
const shortVersions = require('./contextualizers/short-versions')
const redirectsExternal = require('./redirects/external')
const helpToDocs = require('./redirects/help-to-docs')
const languageCodeRedirects = require('./redirects/language-code-redirects')
const handleRedirects = require('./redirects/handle-redirects')
const findPage = require('./find-page')
const blockRobots = require('./block-robots')
const archivedEnterpriseVersionsAssets = require('./archived-enterprise-versions-assets')
const events = require('./events')
const search = require('./search')
const archivedEnterpriseVersions = require('./archived-enterprise-versions')
const robots = require('./robots')
const earlyAccessLinks = require('./contextualizers/early-access-links')
const categoriesForSupport = require('./categories-for-support')
const loaderio = require('./loaderio-verification')
const triggerError = require('./trigger-error')
const releaseNotes = require('./contextualizers/release-notes')
const whatsNewChangelog = require('./contextualizers/whats-new-changelog')
const graphQL = require('./contextualizers/graphql')
const rest = require('./contextualizers/rest')
const webhooks = require('./contextualizers/webhooks')
const layout = require('./contextualizers/layout')
const currentProductTree = require('./contextualizers/current-product-tree')
const genericToc = require('./contextualizers/generic-toc')
const breadcrumbs = require('./contextualizers/breadcrumbs')
const earlyAccessBreadcrumbs = require('./contextualizers/early-access-breadcrumbs')
const features = require('./contextualizers/features')
const productExamples = require('./contextualizers/product-examples')
const devToc = require('./dev-toc')
const featuredLinks = require('./featured-links')
const learningTrack = require('./learning-track')
const isNextRequest = require('./is-next-request')
const next = require('./next')
const renderPage = require('./render-page')
const { NODE_ENV } = process.env
const isDevelopment = NODE_ENV === 'development'
@@ -51,8 +89,8 @@ module.exports = function (app) {
// See https://expressjs.com/en/guide/behind-proxies.html
app.set('trust proxy', 1)
app.use(rateLimit)
app.use(instrument('./handle-invalid-paths'))
app.use(instrument('./handle-next-data-path'))
app.use(instrument(handleInvalidPaths, './handle-invalid-paths'))
app.use(instrument(handleNextDataPath, './handle-next-data-path'))
// *** Security ***
app.use(cors)
@@ -79,28 +117,28 @@ module.exports = function (app) {
// *** Config and context for redirects ***
app.use(reqUtils) // Must come before record-redirect and events
app.use(recordRedirect)
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('./contextualizers/short-versions'))) // Support version shorthands
app.use(instrument(detectLanguage, './detect-language')) // Must come before context, breadcrumbs, find-page, handle-errors, homepages
app.use(asyncMiddleware(instrument(context, './context'))) // Must come before early-access-*, handle-redirects
app.use(asyncMiddleware(instrument(shortVersions, './contextualizers/short-versions'))) // Support version shorthands
// *** Redirects, 3xx responses ***
// I ordered these by use frequency
app.use(connectSlashes(false))
app.use(instrument('./redirects/external'))
app.use(instrument('./redirects/help-to-docs'))
app.use(instrument('./redirects/language-code-redirects')) // Must come before contextualizers
app.use(instrument('./redirects/handle-redirects')) // Must come before contextualizers
app.use(instrument(redirectsExternal, './redirects/external'))
app.use(instrument(helpToDocs, './redirects/help-to-docs'))
app.use(instrument(languageCodeRedirects, './redirects/language-code-redirects')) // Must come before contextualizers
app.use(instrument(handleRedirects, './redirects/handle-redirects')) // Must come before contextualizers
// *** Config and context for rendering ***
app.use(asyncMiddleware(instrument('./find-page'))) // Must come before archived-enterprise-versions, breadcrumbs, featured-links, products, render-page
app.use(instrument('./block-robots'))
app.use(asyncMiddleware(instrument(findPage, './find-page'))) // Must come before archived-enterprise-versions, breadcrumbs, featured-links, products, render-page
app.use(instrument(blockRobots, './block-robots'))
// Check for a dropped connection before proceeding
app.use(haltOnDroppedConnection)
// *** Rendering, 2xx responses ***
// I largely ordered these by use frequency
app.use(asyncMiddleware(instrument('./archived-enterprise-versions-assets'))) // Must come before static/assets
app.use(asyncMiddleware(instrument(archivedEnterpriseVersionsAssets, './archived-enterprise-versions-assets'))) // Must come before static/assets
app.use('/dist', express.static('dist', {
index: false,
etag: false,
@@ -120,49 +158,50 @@ module.exports = function (app) {
lastModified: false,
maxAge: '7 days' // A bit longer since releases are more sparse
}))
app.use('/events', asyncMiddleware(instrument('./events')))
app.use('/search', asyncMiddleware(instrument('./search')))
app.use(asyncMiddleware(instrument('./archived-enterprise-versions')))
app.use(instrument('./robots'))
app.use(/(\/.*)?\/early-access$/, instrument('./contextualizers/early-access-links'))
app.use('/categories.json', asyncMiddleware(instrument('./categories-for-support')))
app.use(instrument('./loaderio-verification'))
app.get('/_500', asyncMiddleware(instrument('./trigger-error')))
app.use('/events', asyncMiddleware(instrument(events, './events')))
app.use('/search', asyncMiddleware(instrument(search, './search')))
app.use(asyncMiddleware(instrument(archivedEnterpriseVersions, './archived-enterprise-versions')))
app.use(instrument(robots, './robots'))
app.use(/(\/.*)?\/early-access$/, instrument(earlyAccessLinks, './contextualizers/early-access-links'))
app.use('/categories.json', asyncMiddleware(instrument(categoriesForSupport, './categories-for-support')))
app.use(instrument(loaderio, './loaderio-verification'))
app.get('/_500', asyncMiddleware(instrument(triggerError, './trigger-error')))
// Check for a dropped connection before proceeding (again)
app.use(haltOnDroppedConnection)
// *** Preparation for render-page: contextualizers ***
app.use(asyncMiddleware(instrument('./contextualizers/release-notes')))
app.use(instrument('./contextualizers/graphql'))
app.use(instrument('./contextualizers/rest'))
app.use(instrument('./contextualizers/webhooks'))
app.use(asyncMiddleware(instrument('./contextualizers/whats-new-changelog')))
app.use(instrument('./contextualizers/layout'))
app.use(instrument('./contextualizers/current-product-tree'))
app.use(asyncMiddleware(instrument('./contextualizers/generic-toc')))
app.use(asyncMiddleware(instrument('./contextualizers/breadcrumbs')))
app.use(asyncMiddleware(instrument('./contextualizers/early-access-breadcrumbs')))
app.use(asyncMiddleware(instrument('./contextualizers/product-examples')))
app.use(asyncMiddleware(instrument(releaseNotes, './contextualizers/release-notes')))
app.use(instrument(graphQL, './contextualizers/graphql'))
app.use(instrument(rest, './contextualizers/rest'))
app.use(instrument(webhooks, './contextualizers/webhooks'))
app.use(asyncMiddleware(instrument(whatsNewChangelog, './contextualizers/whats-new-changelog')))
app.use(instrument(layout, './contextualizers/layout'))
app.use(instrument(currentProductTree, './contextualizers/current-product-tree'))
app.use(asyncMiddleware(instrument(genericToc, './contextualizers/generic-toc')))
app.use(asyncMiddleware(instrument(breadcrumbs, './contextualizers/breadcrumbs')))
app.use(asyncMiddleware(instrument(earlyAccessBreadcrumbs, './contextualizers/early-access-breadcrumbs')))
app.use(asyncMiddleware(instrument(features, './contextualizers/features')))
app.use(asyncMiddleware(instrument(productExamples, './contextualizers/product-examples')))
app.use(asyncMiddleware(instrument('./dev-toc')))
app.use(asyncMiddleware(instrument('./featured-links')))
app.use(asyncMiddleware(instrument('./learning-track')))
app.use(asyncMiddleware(instrument('./is-next-request')))
app.use(asyncMiddleware(instrument(devToc, './dev-toc')))
app.use(asyncMiddleware(instrument(featuredLinks, './featured-links')))
app.use(asyncMiddleware(instrument(learningTrack, './learning-track')))
app.use(asyncMiddleware(instrument(isNextRequest, './is-next-request')))
// *** Headers for pages only ***
app.use(setFastlyCacheHeaders)
// handle serving NextJS bundled code (/_next/*)
if (process.env.FEATURE_NEXTJS) {
app.use(instrument('./next'))
app.use(instrument(next, './next'))
}
// Check for a dropped connection before proceeding (again)
app.use(haltOnDroppedConnection)
// *** Rendering, must go almost last ***
app.get('/*', asyncMiddleware(instrument('./render-page')))
app.get('/*', asyncMiddleware(instrument(renderPage, './render-page')))
// *** Error handling, must go last ***
app.use(handleErrors)

View File

@@ -1,4 +1,5 @@
const externalSites = require('../../lib/redirects/external-sites')
const readJsonFile = require('../../lib/read-json-file')
const externalSites = readJsonFile('./lib/redirects/external-sites.json')
// blanket redirects to external websites
module.exports = function externalRedirects (req, res, next) {

View File

@@ -3,7 +3,7 @@
const fs = require('fs')
const path = require('path')
const { execSync } = require('child_process')
const app = require('../../lib/app')
const createApp = require('../../lib/app')
const port = '4001'
const host = `http://localhost:${port}`
const scrape = require('website-scraper')
@@ -155,7 +155,7 @@ async function main () {
plugins: [new RewriteAssetPathsPlugin(version, tempDirectory)]
}
app.listen(port, async () => {
createApp().listen(port, async () => {
console.log(`started server on ${host}`)
await scrape(scraperOptions).catch(err => {

View File

@@ -12,6 +12,7 @@ const enterpriseServerReleases = require('../../lib/enterprise-server-releases')
const contentPath = path.join(__dirname, '../../content')
const dataPath = path.join(__dirname, '../../data')
const removeUnusedAssetsScript = 'script/remove-unused-assets'
const runRemoveUnusedAssetsScript = require('../remove-unused-assets')
const elseifRegex = /{-?% elsif/
// [start-readme]
@@ -64,7 +65,7 @@ const allFiles = contentFiles.concat(dataFiles)
main()
console.log(`\nRunning ${removeUnusedAssetsScript}...`)
require(path.join(process.cwd(), removeUnusedAssetsScript))
runRemoveUnusedAssetsScript()
function printElseIfFoundWarning (location) {
console.log(`${location} has an 'elsif' condition! Resolve all elsifs by hand, then rerun the script.`)

View File

@@ -53,10 +53,10 @@ const changesFile = path.join(graphqlStaticDir, 'upcoming-changes.json')
const objectsFile = path.join(graphqlStaticDir, 'prerendered-objects.json')
const inputObjectsFile = path.join(graphqlStaticDir, 'prerendered-input-objects.json')
const previews = require(previewsFile)
const changes = require(changesFile)
const objects = require(objectsFile)
const inputObjects = require(inputObjectsFile)
const previews = JSON.parse(fs.readFileSync(previewsFile))
const changes = JSON.parse(fs.readFileSync(changesFile))
const objects = JSON.parse(fs.readFileSync(objectsFile))
const inputObjects = JSON.parse(fs.readFileSync(inputObjectsFile))
// The prerendered objects file for the "old version" contains hardcoded links with the old version number.
// We need to update those links to include the new version to prevent a test from failing.
const regexOldVersion = new RegExp(oldVersion, 'gi')

View File

@@ -8,7 +8,7 @@ const { execSync } = require('child_process')
const graphqlDataDir = path.join(process.cwd(), 'data/graphql')
const graphqlStaticDir = path.join(process.cwd(), 'lib/graphql/static')
const { getContents, listMatchingRefs } = require('../helpers/git-utils')
const dataFilenames = require('./utils/data-filenames')
const dataFilenames = JSON.parse(fs.readFileSync('./utils/data-filenames.json'))
const allVersions = require('../../lib/all-versions')
const processPreviews = require('./utils/process-previews')
const processUpcomingChanges = require('./utils/process-upcoming-changes')

View File

@@ -1,8 +1,9 @@
const { sortBy } = require('lodash')
const { parse, buildASTSchema } = require('graphql')
const helpers = require('./schema-helpers')
const fs = require('fs')
const externalScalars = require('../../../lib/graphql/non-schema-scalars')
const externalScalars = JSON.parse(fs.readFileSync('../../../lib/graphql/non-schema-scalars.json'))
.map(scalar => {
scalar.id = helpers.getId(scalar.name)
scalar.href = helpers.getFullLink('scalars', scalar.id)

View File

@@ -1,5 +1,6 @@
const renderContent = require('../../../lib/render-content')
const graphqlTypes = require('../../../lib/graphql/types')
const fs = require('fs')
const graphqlTypes = JSON.parse(fs.readFileSync('../../../lib/graphql/types.json'))
const {
isScalarType,
isObjectType,

View File

@@ -1,18 +1,17 @@
require('dotenv').config()
// Intentionally require these for both cluster primary and workers
require('./lib/feature-flags')
require('./lib/check-node-version')
require('./lib/handle-exceptions')
const throng = require('throng')
const os = require('os')
const portUsed = require('port-used')
const prefixStreamWrite = require('./lib/prefix-stream-write')
const libApp = require('./lib/app')
const libWarmServer = require('./lib/warm-server')
const createApp = require('./lib/app')
const warmServer = require('./lib/warm-server')
const http = require('http')
// Intentionally require these for both cluster primary and workers
require('./lib/check-node-version')
require('./lib/handle-exceptions')
require('./lib/feature-flags')
const { PORT, NODE_ENV } = process.env
const port = Number(PORT) || 4000
@@ -49,8 +48,7 @@ async function checkPortAvailability () {
}
async function startServer () {
const app = libApp
const warmServer = libWarmServer
const app = createApp()
// If in a deployed environment...
if (NODE_ENV === 'production') {

View File

@@ -4,7 +4,7 @@ const path = require('path')
const sleep = require('await-sleep')
const { latest } = require('../../lib/enterprise-server-releases')
const languages = require('../../lib/languages')
const featureFlags = JSON.parse(fs.readFileSync(path.join(process.cwd(), '/feature-flags.json')))
const featureFlags = JSON.parse(fs.readFileSync(path.join(process.cwd(), './feature-flags.json')))
describe('homepage', () => {
jest.setTimeout(60 * 1000)

View File

@@ -1,13 +1,14 @@
const fs = require('fs')
const path = require('path')
const previewsJson = require('../../lib/graphql/static/previews')
const upcomingChangesJson = require('../../lib/graphql/static/upcoming-changes')
const prerenderedObjectsJson = require('../../lib/graphql/static/prerendered-objects')
const readJsonFile = require('../../lib/read-json-file')
const previewsJson = readJsonFile('./lib/graphql/static/previews.json')
const upcomingChangesJson = readJsonFile('./lib/graphql/static/upcoming-changes.json')
const prerenderedObjectsJson = readJsonFile('./lib/graphql/static/prerendered-objects.json')
const { schemaValidator, previewsValidator, upcomingChangesValidator } = require('../../lib/graphql/validator')
const revalidator = require('revalidator')
const allVersions = Object.values(require('../../lib/all-versions'))
const graphqlVersions = allVersions.map(v => v.miscVersionName)
const graphqlTypes = require('../../lib/graphql/types').map(t => t.kind)
const graphqlTypes = readJsonFile('./lib/graphql/types.json').map(t => t.kind)
describe('graphql json files', () => {
jest.setTimeout(3 * 60 * 1000)

View File

@@ -0,0 +1,7 @@
---
title: Some article only versioned for FPT
versions:
fpt: '*'
ghes: '>2.21'
feature: 'placeholder'
---

View File

@@ -3,8 +3,9 @@ const { createChangelogEntry, cleanPreviewTitle, previewAnchor, prependDatedEntr
const fs = require('fs').promises
const MockDate = require('mockdate')
const readFileAsync = require('../../lib/readfile-async')
const expectedChangelogEntry = require('../fixtures/changelog-entry')
const expectedUpdatedChangelogFile = require('../fixtures/updated-changelog-file')
const readJsonFile = require('../../lib/read-json-file')
const expectedChangelogEntry = readJsonFile('./tests/fixtures/changelog-entry.json')
const expectedUpdatedChangelogFile = readJsonFile('./tests/fixtures/updated-changelog-file.json')
describe('creating a changelog from old schema and new schema', () => {
afterEach(() => {

View File

@@ -0,0 +1,18 @@
const { schema } = require('../../../lib/frontmatter')
// Copy the properties from the frontmatter schema.
const featureVersions = {
properties: {
versions: Object.assign({}, schema.properties.versions)
}
}
// Remove the feature versions properties.
// We don't want to allow features within features! We just want pure versioning.
delete featureVersions.properties.versions.properties.feature
// Call it invalid if any properties other than version properties are found.
featureVersions.additionalProperties = false
featureVersions.properties.versions.additionalProperties = false
module.exports = featureVersions

View File

@@ -3,7 +3,8 @@
const cheerio = require('cheerio')
const supertest = require('supertest')
const app = require('../../lib/app')
const createApp = require('../../lib/app')
const app = createApp()
const helpers = {}

View File

@@ -13,6 +13,7 @@ const { tags } = require('../../lib/liquid-tags/extended-markdown')
const ghesReleaseNotesSchema = require('../helpers/schemas/ghes-release-notes-schema')
const ghaeReleaseNotesSchema = require('../helpers/schemas/ghae-release-notes-schema')
const learningTracksSchema = require('../helpers/schemas/learning-tracks-schema')
const featureVersionsSchema = require('../helpers/schemas/feature-versions-schema')
const renderContent = require('../../lib/render-content')
const getApplicableVersions = require('../../lib/get-applicable-versions')
const { execSync } = require('child_process')
@@ -32,6 +33,7 @@ const glossariesDir = path.join(rootDir, 'data/glossaries')
const ghesReleaseNotesDir = path.join(rootDir, 'data/release-notes/enterprise-server')
const ghaeReleaseNotesDir = path.join(rootDir, 'data/release-notes/github-ae')
const learningTracks = path.join(rootDir, 'data/learning-tracks')
const featureVersionsDir = path.join(rootDir, 'data/features')
const languageCodes = Object.keys(languages)
@@ -187,7 +189,7 @@ const yamlWalkOptions = {
}
// different lint rules apply to different content types
let mdToLint, ymlToLint, ghesReleaseNotesToLint, ghaeReleaseNotesToLint, learningTracksToLint
let mdToLint, ymlToLint, ghesReleaseNotesToLint, ghaeReleaseNotesToLint, learningTracksToLint, featureVersionsToLint
if (!process.env.TEST_TRANSLATION) {
// compile lists of all the files we want to lint
@@ -228,6 +230,11 @@ if (!process.env.TEST_TRANSLATION) {
const learningTracksYamlAbsPaths = walk(learningTracks, yamlWalkOptions).sort()
const learningTracksYamlRelPaths = learningTracksYamlAbsPaths.map(p => slash(path.relative(rootDir, p)))
learningTracksToLint = zip(learningTracksYamlRelPaths, learningTracksYamlAbsPaths)
// Feature versions
const featureVersionsYamlAbsPaths = walk(featureVersionsDir, yamlWalkOptions).sort()
const featureVersionsYamlRelPaths = featureVersionsYamlAbsPaths.map(p => slash(path.relative(rootDir, p)))
featureVersionsToLint = zip(featureVersionsYamlRelPaths, featureVersionsYamlAbsPaths)
} else {
// get all translated markdown or yaml files by comparing files changed to main branch
const changedFilesRelPaths = execSync('git -c diff.renameLimit=10000 diff --name-only origin/main | egrep "^translations/.*/.+.(yml|md)$"', { maxBuffer: 1024 * 1024 * 100 }).toString().split('\n')
@@ -237,7 +244,14 @@ if (!process.env.TEST_TRANSLATION) {
console.log(`Found ${changedFilesRelPaths.length} translated files.`)
const { mdRelPaths = [], ymlRelPaths = [], ghesReleaseNotesRelPaths = [], ghaeReleaseNotesRelPaths = [], learningTracksRelPaths = [] } = groupBy(changedFilesRelPaths, (path) => {
const {
mdRelPaths = [],
ymlRelPaths = [],
ghesReleaseNotesRelPaths = [],
ghaeReleaseNotesRelPaths = [],
learningTracksRelPaths = [],
featureVersionsRelPaths = [],
} = groupBy(changedFilesRelPaths, (path) => {
// separate the changed files to different groups
if (path.endsWith('README.md')) {
return 'throwAway'
@@ -251,13 +265,29 @@ if (!process.env.TEST_TRANSLATION) {
return 'ghaeReleaseNotesRelPaths'
} else if (path.match(/\data\/learning-tracks/)) {
return 'learningTracksRelPaths'
} else if (path.match(/\data\/features/)) {
return 'featureVersionsRelPaths'
} else {
// we aren't linting the rest
return 'throwAway'
}
})
const [mdTuples, ymlTuples, ghesReleaseNotesTuples, ghaeReleaseNotesTuples, learningTracksTuples] = [mdRelPaths, ymlRelPaths, ghesReleaseNotesRelPaths, ghaeReleaseNotesRelPaths, learningTracksRelPaths].map(relPaths => {
const [
mdTuples,
ymlTuples,
ghesReleaseNotesTuples,
ghaeReleaseNotesTuples,
learningTracksTuples,
featureVersionsTuples
] = [
mdRelPaths,
ymlRelPaths,
ghesReleaseNotesRelPaths,
ghaeReleaseNotesRelPaths,
learningTracksRelPaths,
featureVersionsRelPaths
].map(relPaths => {
const absPaths = relPaths.map(p => path.join(rootDir, p))
return zip(relPaths, absPaths)
})
@@ -267,6 +297,7 @@ if (!process.env.TEST_TRANSLATION) {
ghesReleaseNotesToLint = ghesReleaseNotesTuples
ghaeReleaseNotesToLint = ghaeReleaseNotesTuples
learningTracksToLint = learningTracksTuples
featureVersionsToLint = featureVersionsTuples
}
function formatLinkError(message, links) {
@@ -909,6 +940,38 @@ describe('lint learning tracks', () => {
)
})
describe('lint feature versions', () => {
if (featureVersionsToLint.length < 1) return
describe.each(featureVersionsToLint)(
'%s',
(yamlRelPath, yamlAbsPath) => {
let dictionary
beforeAll(async () => {
const fileContents = await readFileAsync(yamlAbsPath, 'utf8')
dictionary = yaml.load(fileContents, { filename: yamlRelPath })
})
it('matches the schema', () => {
const { errors } = revalidator.validate(dictionary, featureVersionsSchema)
const errorMessage = errors.map(error => {
// Make this one message a little more readable than the error we get from revalidator
// when additionalProperties is set to false and an additional prop is found.
const errorToReport = error.message === 'must not exist' && error.actual.feature
? `feature: '${error.actual.feature}'`
: JSON.stringify(error.actual, null, 2)
return `- [${error.property}]: ${errorToReport}, ${error.message}`
})
.join('\n')
expect(errors.length, errorMessage).toBe(0)
})
}
)
})
function validateVersion (version) {
return versionShortNames.includes(version) ||
versionShortNameExceptions.some(exception => version.startsWith(exception))

View File

@@ -1,11 +1,12 @@
const request = require('supertest')
const nock = require('nock')
const cheerio = require('cheerio')
const app = require('../../lib/app')
const createApp = require('../../lib/app')
describe('POST /events', () => {
jest.setTimeout(60 * 1000)
const app = createApp()
let csrfToken = ''
let agent

View File

@@ -126,7 +126,7 @@ describe('header', () => {
const ghe = $(`#homepages a[href="/en/enterprise-server@${latest}/admin"]`)
expect(ghe.length).toBe(1)
expect(ghe.text().trim()).toBe('GitHub Enterprise')
expect(ghe.text().trim()).toBe('Enterprise administrators')
expect(ghe.attr('class').includes('active')).toBe(false)
})

View File

@@ -8,6 +8,8 @@ const builtAssets = require('../../lib/built-asset-urls')
const AZURE_STORAGE_URL = 'githubdocs.azureedge.net'
const CspParse = require('csp-parse')
jest.useFakeTimers()
describe('server', () => {
jest.setTimeout(60 * 1000)

View File

@@ -16,7 +16,7 @@ describe('sidebar', () => {
test('highlights active product on Enterprise pages', async () => {
expect($enterprisePage('.sidebar-products li.sidebar-product').length).toBe(1)
expect($enterprisePage('.sidebar-products li.sidebar-product > a').text().trim()).toBe('GitHub Enterprise')
expect($enterprisePage('.sidebar-products li.sidebar-product > a').text().trim()).toBe('Enterprise administrators')
})
test('highlights active product on GitHub pages', async () => {

View File

@@ -1,4 +1,5 @@
const app = require('../../lib/app')
const createApp = require('../../lib/app')
const app = createApp()
const enterpriseServerReleases = require('../../lib/enterprise-server-releases')
const { get, getDOM } = require('../helpers/supertest')
const supertest = require('supertest')

View File

@@ -2,9 +2,10 @@ const path = require('path')
const { eachOfLimit } = require('async')
const enterpriseServerReleases = require('../../lib/enterprise-server-releases')
const { get } = require('../helpers/supertest')
const restRedirectFixtures = require('../fixtures/rest-redirects')
const graphqlRedirectFixtures = require('../fixtures/graphql-redirects')
const developerRedirectFixtures = require('../fixtures/developer-redirects')
const readJsonFile = require('../../lib/read-json-file')
const restRedirectFixtures = readJsonFile('./tests/fixtures/rest-redirects.json')
const graphqlRedirectFixtures = readJsonFile('./tests/fixtures/graphql-redirects.json')
const developerRedirectFixtures = readJsonFile('./tests/fixtures/developer-redirects.json')
const MAX_CONCURRENT_REQUESTS = 50

View File

@@ -1,7 +1,7 @@
const path = require('path')
const { isPlainObject } = require('lodash')
const supertest = require('supertest')
const app = require('../../lib/app')
const createApp = require('../../lib/app')
const enterpriseServerReleases = require('../../lib/enterprise-server-releases')
const nonEnterpriseDefaultVersion = require('../../lib/non-enterprise-default-version')
const Page = require('../../lib/page')
@@ -103,7 +103,7 @@ describe('redirects', () => {
})
test('are redirected for HEAD requests (not just GET requests)', async () => {
const res = await supertest(app).head('/articles/closing-issues-via-commit-messages/')
const res = await supertest(createApp()).head('/articles/closing-issues-via-commit-messages/')
expect(res.statusCode).toBe(301)
expect(res.headers.location).toBe('/articles/closing-issues-via-commit-messages')
})

View File

@@ -1,7 +1,8 @@
const fs = require('fs')
const path = require('path')
const { head } = require('../helpers/supertest')
const topOldDeveloperSitePaths = JSON.parse(fs.readFileSync(path.join(process.cwd(), 'tests/fixtures/top-old-developer-site-paths.json')))
const readJsonFile = require('../../lib/read-json-file')
const topOldDeveloperSitePaths = readJsonFile('tests/fixtures/top-old-developer-site-paths.json')
jest.useFakeTimers()
describe('developer.github.com redirects', () => {
jest.setTimeout(30 * 60 * 1000)

View File

@@ -1,5 +1,6 @@
require('../../lib/feature-flags')
const ffs = require('../../feature-flags')
const readJsonFile = require('../../lib/read-json-file')
const ffs = readJsonFile('./feature-flags.json')
describe('feature flags', () => {
Object.keys(ffs).forEach(featureName => {

View File

@@ -1,7 +1,9 @@
const { liquid } = require('../../lib/render-content')
const middleware = require('../../middleware/contextualizers/short-versions')
const shortVersionsMiddleware = require('../../middleware/contextualizers/short-versions')
const featureVersionsMiddleware = require('../../middleware/contextualizers/features')
const allVersions = require('../../lib/all-versions')
const enterpriseServerReleases = require('../../lib/enterprise-server-releases')
const loadSiteData = require('../../lib/site-data')
const template = `
{% if currentVersion ver_gt "enterprise-server@2.13" %}up to date{% endif %}
@@ -25,6 +27,10 @@ const negativeVersionsTemplate = `
{% ifversion ghes != 3.1 %} I am not GHES 3.1 {% endif %}
`
const featureVersionsTemplate = `
{% if placeholder %} I am placeholder content {% endif %}
`
describe('liquid template parser', () => {
describe('custom operators', () => {
describe('ver_gt', () => {
@@ -68,7 +74,7 @@ describe('liquid template parser', () => {
allVersions,
enterpriseServerReleases
}
await middleware(req, null, () => {})
await shortVersionsMiddleware(req, null, () => {})
const output = await liquid.parseAndRender(shortVersionsTemplate, req.context)
// We should have TWO results because we are supporting two shortcuts
expect(output.replace(/\s\s+/g, ' ').trim()).toBe('I am FPT I am FTP or GHES < 3.0')
@@ -81,7 +87,7 @@ describe('liquid template parser', () => {
allVersions,
enterpriseServerReleases
}
await middleware(req, null, () => {})
await shortVersionsMiddleware(req, null, () => {})
const output = await liquid.parseAndRender(shortVersionsTemplate, req.context)
expect(output.trim()).toBe('I am GHAE')
})
@@ -93,7 +99,7 @@ describe('liquid template parser', () => {
allVersions,
enterpriseServerReleases
}
await middleware(req, null, () => {})
await shortVersionsMiddleware(req, null, () => {})
const output = await liquid.parseAndRender(shortVersionsTemplate, req.context)
expect(output.replace(/\s\s+/g, ' ').trim()).toBe('I am GHES I am GHES < 3.1 I am FTP or GHES < 3.0')
})
@@ -105,7 +111,7 @@ describe('liquid template parser', () => {
allVersions,
enterpriseServerReleases
}
await middleware(req, null, () => {})
await shortVersionsMiddleware(req, null, () => {})
const output = await liquid.parseAndRender(shortVersionsTemplate, req.context)
expect(output.replace(/\s\s+/g, ' ').trim()).toBe('I am GHES I am GHES < 3.1 I am 3.0 only')
})
@@ -117,7 +123,7 @@ describe('liquid template parser', () => {
allVersions,
enterpriseServerReleases
}
await middleware(req, null, () => {})
await shortVersionsMiddleware(req, null, () => {})
const output = await liquid.parseAndRender(negativeVersionsTemplate, req.context)
expect(output.replace(/\s\s+/g, ' ').trim()).toBe('I am not GHES I am not GHES 3.1')
})
@@ -129,7 +135,7 @@ describe('liquid template parser', () => {
allVersions,
enterpriseServerReleases
}
await middleware(req, null, () => {})
await shortVersionsMiddleware(req, null, () => {})
const output = await liquid.parseAndRender(negativeVersionsTemplate, req.context)
expect(output.replace(/\s\s+/g, ' ').trim()).toBe('I am not GHAE I am not GHES 3.1')
})
@@ -141,9 +147,59 @@ describe('liquid template parser', () => {
allVersions,
enterpriseServerReleases
}
await middleware(req, null, () => {})
await shortVersionsMiddleware(req, null, () => {})
const output = await liquid.parseAndRender(negativeVersionsTemplate, req.context)
expect(output.replace(/\s\s+/g, ' ').trim()).toBe('I am not GHAE')
})
})
describe('feature versions', () => {
// Create a fake req so we can test the feature versions middleware
const req = { language: 'en', query: {} }
let siteData
beforeAll(async () => {
const allSiteData = await loadSiteData()
siteData = allSiteData.en.site
})
test('does not render in FPT because feature is not available in FPT', async () => {
req.context = {
currentVersion: 'free-pro-team@latest',
page: {},
allVersions,
enterpriseServerReleases,
site: siteData
}
await featureVersionsMiddleware(req, null, () => {})
const outputFpt = await liquid.parseAndRender(featureVersionsTemplate, req.context)
expect(outputFpt.includes('placeholder content')).toBe(false)
})
test('renders in GHES because feature is available in GHES', async () => {
req.context = {
currentVersion: `enterprise-server@${enterpriseServerReleases.latest}`,
page: {},
allVersions,
enterpriseServerReleases,
site: siteData
}
await featureVersionsMiddleware(req, null, () => {})
const outputFpt = await liquid.parseAndRender(featureVersionsTemplate, req.context)
expect(outputFpt.includes('placeholder content')).toBe(true)
})
test('renders in GHAE because feature is available in GHAE', async () => {
req.context = {
currentVersion: 'github-ae@latest',
page: {},
allVersions,
enterpriseServerReleases,
site: siteData
}
await featureVersionsMiddleware(req, null, () => {})
const outputFpt = await liquid.parseAndRender(featureVersionsTemplate, req.context)
expect(outputFpt.includes('placeholder content')).toBe(true)
})
})
})

View File

@@ -1,7 +1,8 @@
const path = require('path')
const cheerio = require('cheerio')
const Page = require('../../lib/page')
const prerenderedObjects = require('../../lib/graphql/static/prerendered-objects')
const readJsonFile = require('../../lib/read-json-file')
const prerenderedObjects = readJsonFile('./lib/graphql/static/prerendered-objects.json')
const allVersions = require('../../lib/all-versions')
const enterpriseServerReleases = require('../../lib/enterprise-server-releases')
const nonEnterpriseDefaultVersion = require('../../lib/non-enterprise-default-version')
@@ -534,6 +535,48 @@ describe('Page class', () => {
expect(nonEnterpriseDefaultPlan in page.versions).toBe(false)
expect(page.versions.ghes).toBe('*')
})
test('feature versions frontmatter', async () => {
// This fixture file has the frontmatter:
//
// versions:
// fpt: '*'
// ghes: '*'
// feature: 'placeholder'
//
// and placeholder.yml has:
//
// versions:
// ghes: '<2.22'
// ghae: '*'
//
// So we expect to get the versioning from both.
const page = await Page.init({
relativePath: 'feature-versions-frontmatter.md',
basePath: path.join(__dirname, '../fixtures'),
languageCode: 'en'
})
// Test the raw page data.
expect(page.versions.fpt).toBe('*')
expect(page.versions.ghes).toBe('>2.21')
expect(page.versions.ghae).toBeUndefined()
// The `feature` prop gets deleted by lib/get-applicable-versions, so it's undefined.
expect(page.versions.feature).toBeUndefined()
// Test the resolved versioning, where GHES releases specified in frontmatter and in
// feature versions are combined (i.e., one doesn't overwrite the other).
// We can't test that GHES 2.21 is _not_ included here (which it shouldn't be),
// because lib/get-applicable-versions only returns currently supported versions,
// so as soon as 2.21 is deprecated, a test for that _not_ to exist will not be meaningful.
// But by testing that the _latest_ GHES version is returned, we can ensure that the
// the frontmatter GHES `*` is not being overwritten by the placeholder's GHES `<2.22`.
expect(page.applicableVersions.includes('free-pro-team@latest')).toBe(true)
expect(page.applicableVersions.includes(`enterprise-server@${latest}`)).toBe(true)
expect(page.applicableVersions.includes('github-ae@latest')).toBe(true)
expect(page.applicableVersions.includes('feature')).toBe(false)
expect(page.applicableVersions.includes('placeholder')).toBe(false)
})
})
describe('platform specific content', () => {

View File

@@ -4,6 +4,8 @@ const schema = require('../helpers/schemas/products-schema')
const { getDOM, getJSON } = require('../helpers/supertest')
const nonEnterpriseDefaultVersion = require('../../lib/non-enterprise-default-version')
jest.useFakeTimers()
describe('products module', () => {
test('is an object with product ids as keys', () => {
expect('github' in productMap).toBe(true)
@@ -20,14 +22,12 @@ describe('products module', () => {
})
describe('mobile-only products nav', () => {
jest.setTimeout(5 * 60 * 1000)
test('renders current product on various product pages for each product', async () => {
// Note the unversioned homepage at `/` does not have a product selected in the mobile dropdown
expect((await getDOM('/github'))('#current-product').text().trim()).toBe('GitHub.com')
// Enterprise server
expect((await getDOM('/en/enterprise/admin'))('#current-product').text().trim()).toBe('GitHub Enterprise')
expect((await getDOM('/en/enterprise/admin'))('#current-product').text().trim()).toBe('Enterprise administrators')
expect((await getDOM('/en/enterprise/user/github/setting-up-and-managing-your-github-user-account/setting-your-commit-email-address'))('#current-product').text().trim()).toBe('GitHub.com')
expect((await getDOM('/desktop'))('#current-product').text().trim()).toBe('GitHub Desktop')

View File

@@ -5,6 +5,8 @@ const schema = require('../helpers/schemas/versions-schema')
const { getJSON } = require('../helpers/supertest')
const nonEnterpriseDefaultVersion = require('../../lib/non-enterprise-default-version')
jest.useFakeTimers()
describe('versions module', () => {
test('is an object with versions as keys', () => {
expect(nonEnterpriseDefaultVersion in allVersions).toBe(true)