diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile new file mode 100644 index 0000000000..c37a7152ec --- /dev/null +++ b/.devcontainer/Dockerfile @@ -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 + +# [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 " diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 0000000000..4fc616a8b3 --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -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" +} diff --git a/.eslintrc.js b/.eslintrc.js index 2f67474903..98b63e7f26 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -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: [ { diff --git a/.github/workflows/confirm-internal-staff-work-in-docs.yml b/.github/workflows/confirm-internal-staff-work-in-docs.yml index e07b5348fe..810368f136 100644 --- a/.github/workflows/confirm-internal-staff-work-in-docs.yml +++ b/.github/workflows/confirm-internal-staff-work-in-docs.yml @@ -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. diff --git a/Dockerfile b/Dockerfile index c360b4e2ab..26217363ac 100644 --- a/Dockerfile +++ b/Dockerfile @@ -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 diff --git a/assets/images/help/codespaces/codespaces-commad-palette.png b/assets/images/help/codespaces/codespaces-command-palette.png similarity index 100% rename from assets/images/help/codespaces/codespaces-commad-palette.png rename to assets/images/help/codespaces/codespaces-command-palette.png diff --git a/assets/images/help/profile/username-with-limited-availibilty-text.png b/assets/images/help/profile/username-with-limited-availability-text.png similarity index 100% rename from assets/images/help/profile/username-with-limited-availibilty-text.png rename to assets/images/help/profile/username-with-limited-availability-text.png diff --git a/content/actions/creating-actions/publishing-actions-in-github-marketplace.md b/content/actions/creating-actions/publishing-actions-in-github-marketplace.md index c0de4780ab..da47ef3043 100644 --- a/content/actions/creating-actions/publishing-actions-in-github-marketplace.md +++ b/content/actions/creating-actions/publishing-actions-in-github-marketplace.md @@ -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: diff --git a/content/actions/guides/building-and-testing-xamarin-applications.md b/content/actions/guides/building-and-testing-xamarin-applications.md index 33c7e09590..573ed0509a 100644 --- a/content/actions/guides/building-and-testing-xamarin-applications.md +++ b/content/actions/guides/building-and-testing-xamarin-applications.md @@ -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. diff --git a/content/actions/guides/installing-an-apple-certificate-on-macos-runners-for-xcode-development.md b/content/actions/guides/installing-an-apple-certificate-on-macos-runners-for-xcode-development.md index 64ba326ff7..988e187c5b 100644 --- a/content/actions/guides/installing-an-apple-certificate-on-macos-runners-for-xcode-development.md +++ b/content/actions/guides/installing-an-apple-certificate-on-macos-runners-for-xcode-development.md @@ -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 diff --git a/content/actions/managing-workflow-runs/using-the-visualization-graph.md b/content/actions/managing-workflow-runs/using-the-visualization-graph.md index 2fbeb2a9ae..df1a4ef566 100644 --- a/content/actions/managing-workflow-runs/using-the-visualization-graph.md +++ b/content/actions/managing-workflow-runs/using-the-visualization-graph.md @@ -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 %} diff --git a/content/actions/reference/context-and-expression-syntax-for-github-actions.md b/content/actions/reference/context-and-expression-syntax-for-github-actions.md index a006b007a8..c7a6a8d4e8 100644 --- a/content/actions/reference/context-and-expression-syntax-for-github-actions.md +++ b/content/actions/reference/context-and-expression-syntax-for-github-actions.md @@ -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 diff --git a/content/admin/enterprise-management/configuring-clustering/cluster-network-configuration.md b/content/admin/enterprise-management/configuring-clustering/cluster-network-configuration.md index fad98e50e6..8be13dfbf0 100644 --- a/content/admin/enterprise-management/configuring-clustering/cluster-network-configuration.md +++ b/content/admin/enterprise-management/configuring-clustering/cluster-network-configuration.md @@ -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 diff --git a/content/admin/index.md b/content/admin/index.md index 4c0e68c0a6..0446b77c13 100644 --- a/content/admin/index.md +++ b/content/admin/index.md @@ -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 %}' diff --git a/content/code-security/security-advisories/about-coordinated-disclosure-of-security-vulnerabilities.md b/content/code-security/security-advisories/about-coordinated-disclosure-of-security-vulnerabilities.md index 0b914aecb3..005c46e6b1 100644 --- a/content/code-security/security-advisories/about-coordinated-disclosure-of-security-vulnerabilities.md +++ b/content/code-security/security-advisories/about-coordinated-disclosure-of-security-vulnerabilities.md @@ -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)." - - diff --git a/content/codespaces/codespaces-reference/using-the-command-palette-in-codespaces.md b/content/codespaces/codespaces-reference/using-the-command-palette-in-codespaces.md index 87cc84f7a4..18f6006ca0 100644 --- a/content/codespaces/codespaces-reference/using-the-command-palette-in-codespaces.md +++ b/content/codespaces/codespaces-reference/using-the-command-palette-in-codespaces.md @@ -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 diff --git a/content/codespaces/developing-in-codespaces/creating-a-codespace.md b/content/codespaces/developing-in-codespaces/creating-a-codespace.md index 8b7abccba8..43d0881052 100644 --- a/content/codespaces/developing-in-codespaces/creating-a-codespace.md +++ b/content/codespaces/developing-in-codespaces/creating-a-codespace.md @@ -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) - - - diff --git a/content/codespaces/developing-in-codespaces/forwarding-ports-in-your-codespace.md b/content/codespaces/developing-in-codespaces/forwarding-ports-in-your-codespace.md index 8d8e2c6dec..d1f6e58424 100644 --- a/content/codespaces/developing-in-codespaces/forwarding-ports-in-your-codespace.md +++ b/content/codespaces/developing-in-codespaces/forwarding-ports-in-your-codespace.md @@ -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 %} - diff --git a/content/codespaces/getting-started-with-codespaces/getting-started-with-your-dotnet-project.md b/content/codespaces/getting-started-with-codespaces/getting-started-with-your-dotnet-project.md index 6d460c2c3e..3d65037322 100644 --- a/content/codespaces/getting-started-with-codespaces/getting-started-with-your-dotnet-project.md +++ b/content/codespaces/getting-started-with-codespaces/getting-started-with-your-dotnet-project.md @@ -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. diff --git a/content/codespaces/getting-started-with-codespaces/getting-started-with-your-java-project-in-codespaces.md b/content/codespaces/getting-started-with-codespaces/getting-started-with-your-java-project-in-codespaces.md index 10b0d28d1c..44f895750c 100644 --- a/content/codespaces/getting-started-with-codespaces/getting-started-with-your-java-project-in-codespaces.md +++ b/content/codespaces/getting-started-with-codespaces/getting-started-with-your-java-project-in-codespaces.md @@ -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) diff --git a/content/codespaces/getting-started-with-codespaces/getting-started-with-your-nodejs-project-in-codespaces.md b/content/codespaces/getting-started-with-codespaces/getting-started-with-your-nodejs-project-in-codespaces.md index b6797723d7..12c2b737f3 100644 --- a/content/codespaces/getting-started-with-codespaces/getting-started-with-your-nodejs-project-in-codespaces.md +++ b/content/codespaces/getting-started-with-codespaces/getting-started-with-your-nodejs-project-in-codespaces.md @@ -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) diff --git a/content/codespaces/getting-started-with-codespaces/getting-started-with-your-python-project-in-codespaces.md b/content/codespaces/getting-started-with-codespaces/getting-started-with-your-python-project-in-codespaces.md index 4c6484ba68..6362bcb0d5 100644 --- a/content/codespaces/getting-started-with-codespaces/getting-started-with-your-python-project-in-codespaces.md +++ b/content/codespaces/getting-started-with-codespaces/getting-started-with-your-python-project-in-codespaces.md @@ -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 that’s 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 that’s 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. diff --git a/content/codespaces/managing-codespaces-for-your-organization/index.md b/content/codespaces/managing-codespaces-for-your-organization/index.md index 0411e18061..37002cfcb9 100644 --- a/content/codespaces/managing-codespaces-for-your-organization/index.md +++ b/content/codespaces/managing-codespaces-for-your-organization/index.md @@ -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: diff --git a/content/codespaces/managing-your-codespaces/reviewing-your-security-logs-for-codespaces.md b/content/codespaces/managing-your-codespaces/reviewing-your-security-logs-for-codespaces.md index d80503da9b..2d1a8681dc 100644 --- a/content/codespaces/managing-your-codespaces/reviewing-your-security-logs-for-codespaces.md +++ b/content/codespaces/managing-your-codespaces/reviewing-your-security-logs-for-codespaces.md @@ -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)". diff --git a/content/desktop/contributing-and-collaborating-using-github-desktop/managing-commits/squashing-commits.md b/content/desktop/contributing-and-collaborating-using-github-desktop/managing-commits/squashing-commits.md index 0f92cbae06..fcc1591713 100644 --- a/content/desktop/contributing-and-collaborating-using-github-desktop/managing-commits/squashing-commits.md +++ b/content/desktop/contributing-and-collaborating-using-github-desktop/managing-commits/squashing-commits.md @@ -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 or Shift. ![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 Ctrl or Shift. ![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 %} diff --git a/content/developers/apps/getting-started-with-apps/setting-up-your-development-environment-to-create-a-github-app.md b/content/developers/apps/getting-started-with-apps/setting-up-your-development-environment-to-create-a-github-app.md index c3b3eb9bfa..fc720cac23 100644 --- a/content/developers/apps/getting-started-with-apps/setting-up-your-development-environment-to-create-a-github-app.md +++ b/content/developers/apps/getting-started-with-apps/setting-up-your-development-environment-to-create-a-github-app.md @@ -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) diff --git a/content/github/setting-up-and-managing-your-github-profile/customizing-your-profile/personalizing-your-profile.md b/content/github/setting-up-and-managing-your-github-profile/customizing-your-profile/personalizing-your-profile.md index ce8221c16a..dd3a0a8555 100644 --- a/content/github/setting-up-and-managing-your-github-profile/customizing-your-profile/personalizing-your-profile.md +++ b/content/github/setting-up-and-managing-your-github-profile/customizing-your-profile/personalizing-your-profile.md @@ -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) diff --git a/content/github/site-policy-deprecated/github-ae-data-protection-agreement.md b/content/github/site-policy-deprecated/github-ae-data-protection-agreement.md index 191e7275f8..531ff23c8f 100644 --- a/content/github/site-policy-deprecated/github-ae-data-protection-agreement.md +++ b/content/github/site-policy-deprecated/github-ae-data-protection-agreement.md @@ -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. - diff --git a/content/github/site-policy/dmca-takedown-policy.md b/content/github/site-policy/dmca-takedown-policy.md index b19a3ab1ca..d213e034a7 100644 --- a/content/github/site-policy/dmca-takedown-policy.md +++ b/content/github/site-policy/dmca-takedown-policy.md @@ -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?) diff --git a/content/github/site-policy/github-data-protection-agreement-non-enterprise-customers.md b/content/github/site-policy/github-data-protection-agreement-non-enterprise-customers.md index 10d8e0df84..7aae2a1b58 100644 --- a/content/github/site-policy/github-data-protection-agreement-non-enterprise-customers.md +++ b/content/github/site-policy/github-data-protection-agreement-non-enterprise-customers.md @@ -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; diff --git a/content/github/writing-on-github/editing-and-sharing-content-with-gists/creating-gists.md b/content/github/writing-on-github/editing-and-sharing-content-with-gists/creating-gists.md index bbfe49aefe..0efe033559 100644 --- a/content/github/writing-on-github/editing-and-sharing-content-with-gists/creating-gists.md +++ b/content/github/writing-on-github/editing-and-sharing-content-with-gists/creating-gists.md @@ -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 %} diff --git a/content/graphql/guides/forming-calls-with-graphql.md b/content/graphql/guides/forming-calls-with-graphql.md index 8e709cfd4f..3880f49d99 100644 --- a/content/graphql/guides/forming-calls-with-graphql.md +++ b/content/graphql/guides/forming-calls-with-graphql.md @@ -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) diff --git a/content/organizations/keeping-your-organization-secure/reviewing-the-audit-log-for-your-organization.md b/content/organizations/keeping-your-organization-secure/reviewing-the-audit-log-for-your-organization.md index 160749de7e..eb7cbcf488 100644 --- a/content/organizations/keeping-your-organization-secure/reviewing-the-audit-log-for-your-organization.md +++ b/content/organizations/keeping-your-organization-secure/reviewing-the-audit-log-for-your-organization.md @@ -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)." diff --git a/content/organizations/managing-organization-settings/setting-permissions-for-adding-outside-collaborators.md b/content/organizations/managing-organization-settings/setting-permissions-for-adding-outside-collaborators.md index 208ae660aa..85c64b915b 100644 --- a/content/organizations/managing-organization-settings/setting-permissions-for-adding-outside-collaborators.md +++ b/content/organizations/managing-organization-settings/setting-permissions-for-adding-outside-collaborators.md @@ -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 diff --git a/content/rest/reference/code-scanning.md b/content/rest/reference/code-scanning.md index 59d8c24e26..e3504cd0f7 100644 --- a/content/rest/reference/code-scanning.md +++ b/content/rest/reference/code-scanning.md @@ -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)." ``` { diff --git a/content/rest/reference/permissions-required-for-github-apps.md b/content/rest/reference/permissions-required-for-github-apps.md index 1332afe20a..02c9c2df03 100644 --- a/content/rest/reference/permissions-required-for-github-apps.md +++ b/content/rest/reference/permissions-required-for-github-apps.md @@ -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" %} diff --git a/data/features/README.md b/data/features/README.md new file mode 100644 index 0000000000..c2fca9380d --- /dev/null +++ b/data/features/README.md @@ -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! diff --git a/data/features/placeholder.yml b/data/features/placeholder.yml new file mode 100644 index 0000000000..a133861e35 --- /dev/null +++ b/data/features/placeholder.yml @@ -0,0 +1,4 @@ +# Do not delete! Used by tests. +versions: + ghes: '>3.0' + ghae: '*' diff --git a/data/reusables/actions/visualization-beta.md b/data/reusables/actions/visualization-beta.md deleted file mode 100644 index da3ac2b0d2..0000000000 --- a/data/reusables/actions/visualization-beta.md +++ /dev/null @@ -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 %} diff --git a/data/reusables/command_line/providing-token-as-password.md b/data/reusables/command_line/providing-token-as-password.md index 54cb54e48e..a938fcfd9c 100644 --- a/data/reusables/command_line/providing-token-as-password.md +++ b/data/reusables/command_line/providing-token-as-password.md @@ -4,6 +4,6 @@ For example, on the command line you would enter the following: ```shell $ git clone https://{% data variables.command_line.codeblock %}/username/repo.git -Username: your_username +Username: your_username Password: your_token ``` diff --git a/data/reusables/gated-features/restict-add-collaborator.md b/data/reusables/gated-features/restrict-add-collaborator.md similarity index 100% rename from data/reusables/gated-features/restict-add-collaborator.md rename to data/reusables/gated-features/restrict-add-collaborator.md diff --git a/lib/app.js b/lib/app.js index 849e2814e9..3cfe51244c 100644 --- a/lib/app.js +++ b/lib/app.js @@ -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 diff --git a/lib/feature-flags.js b/lib/feature-flags.js index 970599f073..f125b40b8c 100644 --- a/lib/feature-flags.js +++ b/lib/feature-flags.js @@ -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]) => { diff --git a/lib/frontmatter.js b/lib/frontmatter.js index 3fb6fafe85..b93f40608e 100644 --- a/lib/frontmatter.js +++ b/lib/frontmatter.js @@ -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, diff --git a/lib/get-applicable-versions.js b/lib/get-applicable-versions.js index 5ac635e501..d7adb24080 100644 --- a/lib/get-applicable-versions.js +++ b/lib/get-applicable-versions.js @@ -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 diff --git a/lib/instrument-middleware.js b/lib/instrument-middleware.js index 0ac2d80c8b..3e566b052a 100644 --- a/lib/instrument-middleware.js +++ b/lib/instrument-middleware.js @@ -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' diff --git a/lib/read-json-file.js b/lib/read-json-file.js new file mode 100644 index 0000000000..353bdd078b --- /dev/null +++ b/lib/read-json-file.js @@ -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' + ) + ) +} diff --git a/lib/redirects/precompile.js b/lib/redirects/precompile.js index 9112bb32f4..cd3b4dfa75 100755 --- a/lib/redirects/precompile.js +++ b/lib/redirects/precompile.js @@ -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 = {} diff --git a/lib/render-content/plugins/rewrite-local-links.js b/lib/render-content/plugins/rewrite-local-links.js index 5e121c333f..a357a312ed 100644 --- a/lib/render-content/plugins/rewrite-local-links.js +++ b/lib/render-content/plugins/rewrite-local-links.js @@ -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 tags with an href that starts with `/` const matcher = node => ( diff --git a/lib/rest/static/decorated/api.github.com.json b/lib/rest/static/decorated/api.github.com.json index 3f499b29b5..9188767a36 100644 --- a/lib/rest/static/decorated/api.github.com.json +++ b/lib/rest/static/decorated/api.github.com.json @@ -24451,7 +24451,7 @@ } ], "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" @@ -24472,7 +24472,7 @@ "categoryLabel": "Billing", "notes": [], "bodyParameters": [], - "descriptionHTML": "

Gets the free and paid storage usued for GitHub Packages in gigabytes.

\n

Paid minutes only apply to packages stored for private repositories. For more information, see \"Managing billing for GitHub Packages.\"

\n

Access tokens must have the repo or admin:org scope.

", + "descriptionHTML": "

Gets the free and paid storage used for GitHub Packages in gigabytes.

\n

Paid minutes only apply to packages stored for private repositories. For more information, see \"Managing billing for GitHub Packages.\"

\n

Access tokens must have the repo or admin:org scope.

", "responses": [ { "httpStatusCode": "200", @@ -31588,6 +31588,116 @@ "rawDescription": "Can be `public` or `private`. If your organization is associated with an enterprise account using GitHub Enterprise Cloud or GitHub Enterprise Server 2.20+, `visibility` can also be `internal`. The `visibility` parameter overrides the `private` parameter when you use both along with the `nebula-preview` preview header.", "childParamsGroups": [] }, + "security_and_analysis": { + "type": "object or nullable", + "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.\" 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.

", + "name": "status", + "in": "body", + "rawType": "string", + "rawDescription": "Can be `enabled` or `disabled`.", + "childParamsGroups": [] + } + }, + "name": "advanced_security", + "in": "body", + "rawType": "object", + "rawDescription": "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.", + "childParamsGroups": [ + { + "parentName": "advanced_security", + "parentType": "object", + "id": "advanced_security-object", + "params": [ + { + "type": "string", + "description": "

Can be enabled or disabled.

", + "name": "status", + "in": "body", + "rawType": "string", + "rawDescription": "Can be `enabled` or `disabled`.", + "childParamsGroups": [] + } + ] + } + ] + } + }, + "name": "security_and_analysis", + "in": "body", + "rawType": "object", + "rawDescription": "Specify which security and analysis features to enable or disable.", + "childParamsGroups": [ + { + "parentName": "security_and_analysis", + "parentType": "object", + "id": "security_and_analysis-object", + "params": [ + { + "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.\" 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.

", + "name": "status", + "in": "body", + "rawType": "string", + "rawDescription": "Can be `enabled` or `disabled`.", + "childParamsGroups": [] + } + }, + "name": "advanced_security", + "in": "body", + "rawType": "object", + "rawDescription": "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.", + "childParamsGroups": [ + { + "parentName": "advanced_security", + "parentType": "object", + "id": "advanced_security-object", + "params": [ + { + "type": "string", + "description": "

Can be enabled or disabled.

", + "name": "status", + "in": "body", + "rawType": "string", + "rawDescription": "Can be `enabled` or `disabled`.", + "childParamsGroups": [] + } + ] + } + ] + } + ] + }, + { + "parentName": "advanced_security", + "parentType": "object", + "id": "advanced_security-object", + "params": [ + { + "type": "string", + "description": "

Can be enabled or disabled.

", + "name": "status", + "in": "body", + "rawType": "string", + "rawDescription": "Can be `enabled` or `disabled`.", + "childParamsGroups": [] + } + ] + } + ] + }, "has_issues": { "type": "boolean", "description": "

Either true to enable issues for this repository or false to disable them.

", @@ -31726,6 +31836,34 @@ "categoryLabel": "Repos", "notes": [], "descriptionHTML": "

Note: To edit a repository's topics, use the Replace all repository topics endpoint.

", + "responses": [ + { + "httpStatusCode": "200", + "httpStatusMessage": "OK", + "description": "Response", + "payload": "
{\n  \"id\": 1296269,\n  \"node_id\": \"MDEwOlJlcG9zaXRvcnkxMjk2MjY5\",\n  \"name\": \"Hello-World\",\n  \"full_name\": \"octocat/Hello-World\",\n  \"owner\": {\n    \"login\": \"octocat\",\n    \"id\": 1,\n    \"node_id\": \"MDQ6VXNlcjE=\",\n    \"avatar_url\": \"https://github.com/images/error/octocat_happy.gif\",\n    \"gravatar_id\": \"\",\n    \"url\": \"https://api.github.com/users/octocat\",\n    \"html_url\": \"https://github.com/octocat\",\n    \"followers_url\": \"https://api.github.com/users/octocat/followers\",\n    \"following_url\": \"https://api.github.com/users/octocat/following{/other_user}\",\n    \"gists_url\": \"https://api.github.com/users/octocat/gists{/gist_id}\",\n    \"starred_url\": \"https://api.github.com/users/octocat/starred{/owner}{/repo}\",\n    \"subscriptions_url\": \"https://api.github.com/users/octocat/subscriptions\",\n    \"organizations_url\": \"https://api.github.com/users/octocat/orgs\",\n    \"repos_url\": \"https://api.github.com/users/octocat/repos\",\n    \"events_url\": \"https://api.github.com/users/octocat/events{/privacy}\",\n    \"received_events_url\": \"https://api.github.com/users/octocat/received_events\",\n    \"type\": \"User\",\n    \"site_admin\": false\n  },\n  \"private\": false,\n  \"html_url\": \"https://github.com/octocat/Hello-World\",\n  \"description\": \"This your first repo!\",\n  \"fork\": false,\n  \"url\": \"https://api.github.com/repos/octocat/Hello-World\",\n  \"archive_url\": \"https://api.github.com/repos/octocat/Hello-World/{archive_format}{/ref}\",\n  \"assignees_url\": \"https://api.github.com/repos/octocat/Hello-World/assignees{/user}\",\n  \"blobs_url\": \"https://api.github.com/repos/octocat/Hello-World/git/blobs{/sha}\",\n  \"branches_url\": \"https://api.github.com/repos/octocat/Hello-World/branches{/branch}\",\n  \"collaborators_url\": \"https://api.github.com/repos/octocat/Hello-World/collaborators{/collaborator}\",\n  \"comments_url\": \"https://api.github.com/repos/octocat/Hello-World/comments{/number}\",\n  \"commits_url\": \"https://api.github.com/repos/octocat/Hello-World/commits{/sha}\",\n  \"compare_url\": \"https://api.github.com/repos/octocat/Hello-World/compare/{base}...{head}\",\n  \"contents_url\": \"https://api.github.com/repos/octocat/Hello-World/contents/{+path}\",\n  \"contributors_url\": \"https://api.github.com/repos/octocat/Hello-World/contributors\",\n  \"deployments_url\": \"https://api.github.com/repos/octocat/Hello-World/deployments\",\n  \"downloads_url\": \"https://api.github.com/repos/octocat/Hello-World/downloads\",\n  \"events_url\": \"https://api.github.com/repos/octocat/Hello-World/events\",\n  \"forks_url\": \"https://api.github.com/repos/octocat/Hello-World/forks\",\n  \"git_commits_url\": \"https://api.github.com/repos/octocat/Hello-World/git/commits{/sha}\",\n  \"git_refs_url\": \"https://api.github.com/repos/octocat/Hello-World/git/refs{/sha}\",\n  \"git_tags_url\": \"https://api.github.com/repos/octocat/Hello-World/git/tags{/sha}\",\n  \"git_url\": \"git:github.com/octocat/Hello-World.git\",\n  \"issue_comment_url\": \"https://api.github.com/repos/octocat/Hello-World/issues/comments{/number}\",\n  \"issue_events_url\": \"https://api.github.com/repos/octocat/Hello-World/issues/events{/number}\",\n  \"issues_url\": \"https://api.github.com/repos/octocat/Hello-World/issues{/number}\",\n  \"keys_url\": \"https://api.github.com/repos/octocat/Hello-World/keys{/key_id}\",\n  \"labels_url\": \"https://api.github.com/repos/octocat/Hello-World/labels{/name}\",\n  \"languages_url\": \"https://api.github.com/repos/octocat/Hello-World/languages\",\n  \"merges_url\": \"https://api.github.com/repos/octocat/Hello-World/merges\",\n  \"milestones_url\": \"https://api.github.com/repos/octocat/Hello-World/milestones{/number}\",\n  \"notifications_url\": \"https://api.github.com/repos/octocat/Hello-World/notifications{?since,all,participating}\",\n  \"pulls_url\": \"https://api.github.com/repos/octocat/Hello-World/pulls{/number}\",\n  \"releases_url\": \"https://api.github.com/repos/octocat/Hello-World/releases{/id}\",\n  \"ssh_url\": \"git@github.com:octocat/Hello-World.git\",\n  \"stargazers_url\": \"https://api.github.com/repos/octocat/Hello-World/stargazers\",\n  \"statuses_url\": \"https://api.github.com/repos/octocat/Hello-World/statuses/{sha}\",\n  \"subscribers_url\": \"https://api.github.com/repos/octocat/Hello-World/subscribers\",\n  \"subscription_url\": \"https://api.github.com/repos/octocat/Hello-World/subscription\",\n  \"tags_url\": \"https://api.github.com/repos/octocat/Hello-World/tags\",\n  \"teams_url\": \"https://api.github.com/repos/octocat/Hello-World/teams\",\n  \"trees_url\": \"https://api.github.com/repos/octocat/Hello-World/git/trees{/sha}\",\n  \"clone_url\": \"https://github.com/octocat/Hello-World.git\",\n  \"mirror_url\": \"git:git.example.com/octocat/Hello-World\",\n  \"hooks_url\": \"https://api.github.com/repos/octocat/Hello-World/hooks\",\n  \"svn_url\": \"https://svn.github.com/octocat/Hello-World\",\n  \"homepage\": \"https://github.com\",\n  \"license\": {\n    \"key\": \"mit\",\n    \"name\": \"MIT License\",\n    \"url\": \"https://api.github.com/licenses/mit\",\n    \"spdx_id\": \"MIT\",\n    \"node_id\": \"MDc6TGljZW5zZW1pdA==\",\n    \"html_url\": \"https://github.com/licenses/mit\"\n  },\n  \"language\": null,\n  \"forks_count\": 9,\n  \"forks\": 9,\n  \"stargazers_count\": 80,\n  \"watchers_count\": 80,\n  \"watchers\": 80,\n  \"size\": 108,\n  \"default_branch\": \"master\",\n  \"open_issues_count\": 0,\n  \"open_issues\": 0,\n  \"is_template\": false,\n  \"topics\": [\n    \"octocat\",\n    \"atom\",\n    \"electron\",\n    \"api\"\n  ],\n  \"has_issues\": true,\n  \"has_projects\": true,\n  \"has_wiki\": true,\n  \"has_pages\": false,\n  \"has_downloads\": true,\n  \"archived\": false,\n  \"disabled\": false,\n  \"visibility\": \"public\",\n  \"pushed_at\": \"2011-01-26T19:06:43Z\",\n  \"created_at\": \"2011-01-26T19:01:12Z\",\n  \"updated_at\": \"2011-01-26T19:14:43Z\",\n  \"permissions\": {\n    \"pull\": true,\n    \"push\": false,\n    \"admin\": false\n  },\n  \"allow_rebase_merge\": true,\n  \"template_repository\": {\n    \"id\": 1296269,\n    \"node_id\": \"MDEwOlJlcG9zaXRvcnkxMjk2MjY5\",\n    \"name\": \"Hello-World-Template\",\n    \"full_name\": \"octocat/Hello-World-Template\",\n    \"owner\": {\n      \"login\": \"octocat\",\n      \"id\": 1,\n      \"node_id\": \"MDQ6VXNlcjE=\",\n      \"avatar_url\": \"https://github.com/images/error/octocat_happy.gif\",\n      \"gravatar_id\": \"\",\n      \"url\": \"https://api.github.com/users/octocat\",\n      \"html_url\": \"https://github.com/octocat\",\n      \"followers_url\": \"https://api.github.com/users/octocat/followers\",\n      \"following_url\": \"https://api.github.com/users/octocat/following{/other_user}\",\n      \"gists_url\": \"https://api.github.com/users/octocat/gists{/gist_id}\",\n      \"starred_url\": \"https://api.github.com/users/octocat/starred{/owner}{/repo}\",\n      \"subscriptions_url\": \"https://api.github.com/users/octocat/subscriptions\",\n      \"organizations_url\": \"https://api.github.com/users/octocat/orgs\",\n      \"repos_url\": \"https://api.github.com/users/octocat/repos\",\n      \"events_url\": \"https://api.github.com/users/octocat/events{/privacy}\",\n      \"received_events_url\": \"https://api.github.com/users/octocat/received_events\",\n      \"type\": \"User\",\n      \"site_admin\": false\n    },\n    \"private\": false,\n    \"html_url\": \"https://github.com/octocat/Hello-World-Template\",\n    \"description\": \"This your first repo!\",\n    \"fork\": false,\n    \"url\": \"https://api.github.com/repos/octocat/Hello-World-Template\",\n    \"archive_url\": \"https://api.github.com/repos/octocat/Hello-World-Template/{archive_format}{/ref}\",\n    \"assignees_url\": \"https://api.github.com/repos/octocat/Hello-World-Template/assignees{/user}\",\n    \"blobs_url\": \"https://api.github.com/repos/octocat/Hello-World-Template/git/blobs{/sha}\",\n    \"branches_url\": \"https://api.github.com/repos/octocat/Hello-World-Template/branches{/branch}\",\n    \"collaborators_url\": \"https://api.github.com/repos/octocat/Hello-World-Template/collaborators{/collaborator}\",\n    \"comments_url\": \"https://api.github.com/repos/octocat/Hello-World-Template/comments{/number}\",\n    \"commits_url\": \"https://api.github.com/repos/octocat/Hello-World-Template/commits{/sha}\",\n    \"compare_url\": \"https://api.github.com/repos/octocat/Hello-World-Template/compare/{base}...{head}\",\n    \"contents_url\": \"https://api.github.com/repos/octocat/Hello-World-Template/contents/{+path}\",\n    \"contributors_url\": \"https://api.github.com/repos/octocat/Hello-World-Template/contributors\",\n    \"deployments_url\": \"https://api.github.com/repos/octocat/Hello-World-Template/deployments\",\n    \"downloads_url\": \"https://api.github.com/repos/octocat/Hello-World-Template/downloads\",\n    \"events_url\": \"https://api.github.com/repos/octocat/Hello-World-Template/events\",\n    \"forks_url\": \"https://api.github.com/repos/octocat/Hello-World-Template/forks\",\n    \"git_commits_url\": \"https://api.github.com/repos/octocat/Hello-World-Template/git/commits{/sha}\",\n    \"git_refs_url\": \"https://api.github.com/repos/octocat/Hello-World-Template/git/refs{/sha}\",\n    \"git_tags_url\": \"https://api.github.com/repos/octocat/Hello-World-Template/git/tags{/sha}\",\n    \"git_url\": \"git:github.com/octocat/Hello-World-Template.git\",\n    \"issue_comment_url\": \"https://api.github.com/repos/octocat/Hello-World-Template/issues/comments{/number}\",\n    \"issue_events_url\": \"https://api.github.com/repos/octocat/Hello-World-Template/issues/events{/number}\",\n    \"issues_url\": \"https://api.github.com/repos/octocat/Hello-World-Template/issues{/number}\",\n    \"keys_url\": \"https://api.github.com/repos/octocat/Hello-World-Template/keys{/key_id}\",\n    \"labels_url\": \"https://api.github.com/repos/octocat/Hello-World-Template/labels{/name}\",\n    \"languages_url\": \"https://api.github.com/repos/octocat/Hello-World-Template/languages\",\n    \"merges_url\": \"https://api.github.com/repos/octocat/Hello-World-Template/merges\",\n    \"milestones_url\": \"https://api.github.com/repos/octocat/Hello-World-Template/milestones{/number}\",\n    \"notifications_url\": \"https://api.github.com/repos/octocat/Hello-World-Template/notifications{?since,all,participating}\",\n    \"pulls_url\": \"https://api.github.com/repos/octocat/Hello-World-Template/pulls{/number}\",\n    \"releases_url\": \"https://api.github.com/repos/octocat/Hello-World-Template/releases{/id}\",\n    \"ssh_url\": \"git@github.com:octocat/Hello-World-Template.git\",\n    \"stargazers_url\": \"https://api.github.com/repos/octocat/Hello-World-Template/stargazers\",\n    \"statuses_url\": \"https://api.github.com/repos/octocat/Hello-World-Template/statuses/{sha}\",\n    \"subscribers_url\": \"https://api.github.com/repos/octocat/Hello-World-Template/subscribers\",\n    \"subscription_url\": \"https://api.github.com/repos/octocat/Hello-World-Template/subscription\",\n    \"tags_url\": \"https://api.github.com/repos/octocat/Hello-World-Template/tags\",\n    \"teams_url\": \"https://api.github.com/repos/octocat/Hello-World-Template/teams\",\n    \"trees_url\": \"https://api.github.com/repos/octocat/Hello-World-Template/git/trees{/sha}\",\n    \"clone_url\": \"https://github.com/octocat/Hello-World-Template.git\",\n    \"mirror_url\": \"git:git.example.com/octocat/Hello-World-Template\",\n    \"hooks_url\": \"https://api.github.com/repos/octocat/Hello-World-Template/hooks\",\n    \"svn_url\": \"https://svn.github.com/octocat/Hello-World-Template\",\n    \"homepage\": \"https://github.com\",\n    \"language\": null,\n    \"forks\": 9,\n    \"forks_count\": 9,\n    \"stargazers_count\": 80,\n    \"watchers_count\": 80,\n    \"watchers\": 80,\n    \"size\": 108,\n    \"default_branch\": \"master\",\n    \"open_issues\": 0,\n    \"open_issues_count\": 0,\n    \"is_template\": true,\n    \"license\": {\n      \"key\": \"mit\",\n      \"name\": \"MIT License\",\n      \"url\": \"https://api.github.com/licenses/mit\",\n      \"spdx_id\": \"MIT\",\n      \"node_id\": \"MDc6TGljZW5zZW1pdA==\",\n      \"html_url\": \"https://api.github.com/licenses/mit\"\n    },\n    \"topics\": [\n      \"octocat\",\n      \"atom\",\n      \"electron\",\n      \"api\"\n    ],\n    \"has_issues\": true,\n    \"has_projects\": true,\n    \"has_wiki\": true,\n    \"has_pages\": false,\n    \"has_downloads\": true,\n    \"archived\": false,\n    \"disabled\": false,\n    \"visibility\": \"public\",\n    \"pushed_at\": \"2011-01-26T19:06:43Z\",\n    \"created_at\": \"2011-01-26T19:01:12Z\",\n    \"updated_at\": \"2011-01-26T19:14:43Z\",\n    \"permissions\": {\n      \"admin\": false,\n      \"push\": false,\n      \"pull\": true\n    },\n    \"allow_rebase_merge\": true,\n    \"temp_clone_token\": \"ABTLWHOULUVAXGTRYU7OC2876QJ2O\",\n    \"allow_squash_merge\": true,\n    \"delete_branch_on_merge\": true,\n    \"allow_merge_commit\": true,\n    \"subscribers_count\": 42,\n    \"network_count\": 0\n  },\n  \"temp_clone_token\": \"ABTLWHOULUVAXGTRYU7OC2876QJ2O\",\n  \"allow_squash_merge\": true,\n  \"delete_branch_on_merge\": true,\n  \"allow_merge_commit\": true,\n  \"subscribers_count\": 42,\n  \"network_count\": 0,\n  \"organization\": {\n    \"login\": \"octocat\",\n    \"id\": 1,\n    \"node_id\": \"MDQ6VXNlcjE=\",\n    \"avatar_url\": \"https://github.com/images/error/octocat_happy.gif\",\n    \"gravatar_id\": \"\",\n    \"url\": \"https://api.github.com/users/octocat\",\n    \"html_url\": \"https://github.com/octocat\",\n    \"followers_url\": \"https://api.github.com/users/octocat/followers\",\n    \"following_url\": \"https://api.github.com/users/octocat/following{/other_user}\",\n    \"gists_url\": \"https://api.github.com/users/octocat/gists{/gist_id}\",\n    \"starred_url\": \"https://api.github.com/users/octocat/starred{/owner}{/repo}\",\n    \"subscriptions_url\": \"https://api.github.com/users/octocat/subscriptions\",\n    \"organizations_url\": \"https://api.github.com/users/octocat/orgs\",\n    \"repos_url\": \"https://api.github.com/users/octocat/repos\",\n    \"events_url\": \"https://api.github.com/users/octocat/events{/privacy}\",\n    \"received_events_url\": \"https://api.github.com/users/octocat/received_events\",\n    \"type\": \"Organization\",\n    \"site_admin\": false\n  },\n  \"parent\": {\n    \"id\": 1296269,\n    \"node_id\": \"MDEwOlJlcG9zaXRvcnkxMjk2MjY5\",\n    \"name\": \"Hello-World\",\n    \"full_name\": \"octocat/Hello-World\",\n    \"owner\": {\n      \"login\": \"octocat\",\n      \"id\": 1,\n      \"node_id\": \"MDQ6VXNlcjE=\",\n      \"avatar_url\": \"https://github.com/images/error/octocat_happy.gif\",\n      \"gravatar_id\": \"\",\n      \"url\": \"https://api.github.com/users/octocat\",\n      \"html_url\": \"https://github.com/octocat\",\n      \"followers_url\": \"https://api.github.com/users/octocat/followers\",\n      \"following_url\": \"https://api.github.com/users/octocat/following{/other_user}\",\n      \"gists_url\": \"https://api.github.com/users/octocat/gists{/gist_id}\",\n      \"starred_url\": \"https://api.github.com/users/octocat/starred{/owner}{/repo}\",\n      \"subscriptions_url\": \"https://api.github.com/users/octocat/subscriptions\",\n      \"organizations_url\": \"https://api.github.com/users/octocat/orgs\",\n      \"repos_url\": \"https://api.github.com/users/octocat/repos\",\n      \"events_url\": \"https://api.github.com/users/octocat/events{/privacy}\",\n      \"received_events_url\": \"https://api.github.com/users/octocat/received_events\",\n      \"type\": \"User\",\n      \"site_admin\": false\n    },\n    \"private\": false,\n    \"html_url\": \"https://github.com/octocat/Hello-World\",\n    \"description\": \"This your first repo!\",\n    \"fork\": false,\n    \"url\": \"https://api.github.com/repos/octocat/Hello-World\",\n    \"archive_url\": \"https://api.github.com/repos/octocat/Hello-World/{archive_format}{/ref}\",\n    \"assignees_url\": \"https://api.github.com/repos/octocat/Hello-World/assignees{/user}\",\n    \"blobs_url\": \"https://api.github.com/repos/octocat/Hello-World/git/blobs{/sha}\",\n    \"branches_url\": \"https://api.github.com/repos/octocat/Hello-World/branches{/branch}\",\n    \"collaborators_url\": \"https://api.github.com/repos/octocat/Hello-World/collaborators{/collaborator}\",\n    \"comments_url\": \"https://api.github.com/repos/octocat/Hello-World/comments{/number}\",\n    \"commits_url\": \"https://api.github.com/repos/octocat/Hello-World/commits{/sha}\",\n    \"compare_url\": \"https://api.github.com/repos/octocat/Hello-World/compare/{base}...{head}\",\n    \"contents_url\": \"https://api.github.com/repos/octocat/Hello-World/contents/{+path}\",\n    \"contributors_url\": \"https://api.github.com/repos/octocat/Hello-World/contributors\",\n    \"deployments_url\": \"https://api.github.com/repos/octocat/Hello-World/deployments\",\n    \"downloads_url\": \"https://api.github.com/repos/octocat/Hello-World/downloads\",\n    \"events_url\": \"https://api.github.com/repos/octocat/Hello-World/events\",\n    \"forks_url\": \"https://api.github.com/repos/octocat/Hello-World/forks\",\n    \"git_commits_url\": \"https://api.github.com/repos/octocat/Hello-World/git/commits{/sha}\",\n    \"git_refs_url\": \"https://api.github.com/repos/octocat/Hello-World/git/refs{/sha}\",\n    \"git_tags_url\": \"https://api.github.com/repos/octocat/Hello-World/git/tags{/sha}\",\n    \"git_url\": \"git:github.com/octocat/Hello-World.git\",\n    \"issue_comment_url\": \"https://api.github.com/repos/octocat/Hello-World/issues/comments{/number}\",\n    \"issue_events_url\": \"https://api.github.com/repos/octocat/Hello-World/issues/events{/number}\",\n    \"issues_url\": \"https://api.github.com/repos/octocat/Hello-World/issues{/number}\",\n    \"keys_url\": \"https://api.github.com/repos/octocat/Hello-World/keys{/key_id}\",\n    \"labels_url\": \"https://api.github.com/repos/octocat/Hello-World/labels{/name}\",\n    \"languages_url\": \"https://api.github.com/repos/octocat/Hello-World/languages\",\n    \"merges_url\": \"https://api.github.com/repos/octocat/Hello-World/merges\",\n    \"milestones_url\": \"https://api.github.com/repos/octocat/Hello-World/milestones{/number}\",\n    \"notifications_url\": \"https://api.github.com/repos/octocat/Hello-World/notifications{?since,all,participating}\",\n    \"pulls_url\": \"https://api.github.com/repos/octocat/Hello-World/pulls{/number}\",\n    \"releases_url\": \"https://api.github.com/repos/octocat/Hello-World/releases{/id}\",\n    \"ssh_url\": \"git@github.com:octocat/Hello-World.git\",\n    \"stargazers_url\": \"https://api.github.com/repos/octocat/Hello-World/stargazers\",\n    \"statuses_url\": \"https://api.github.com/repos/octocat/Hello-World/statuses/{sha}\",\n    \"subscribers_url\": \"https://api.github.com/repos/octocat/Hello-World/subscribers\",\n    \"subscription_url\": \"https://api.github.com/repos/octocat/Hello-World/subscription\",\n    \"tags_url\": \"https://api.github.com/repos/octocat/Hello-World/tags\",\n    \"teams_url\": \"https://api.github.com/repos/octocat/Hello-World/teams\",\n    \"trees_url\": \"https://api.github.com/repos/octocat/Hello-World/git/trees{/sha}\",\n    \"clone_url\": \"https://github.com/octocat/Hello-World.git\",\n    \"mirror_url\": \"git:git.example.com/octocat/Hello-World\",\n    \"hooks_url\": \"https://api.github.com/repos/octocat/Hello-World/hooks\",\n    \"svn_url\": \"https://svn.github.com/octocat/Hello-World\",\n    \"homepage\": \"https://github.com\",\n    \"language\": null,\n    \"forks_count\": 9,\n    \"stargazers_count\": 80,\n    \"watchers_count\": 80,\n    \"size\": 108,\n    \"default_branch\": \"master\",\n    \"open_issues_count\": 0,\n    \"is_template\": true,\n    \"topics\": [\n      \"octocat\",\n      \"atom\",\n      \"electron\",\n      \"api\"\n    ],\n    \"has_issues\": true,\n    \"has_projects\": true,\n    \"has_wiki\": true,\n    \"has_pages\": false,\n    \"has_downloads\": true,\n    \"archived\": false,\n    \"disabled\": false,\n    \"visibility\": \"public\",\n    \"pushed_at\": \"2011-01-26T19:06:43Z\",\n    \"created_at\": \"2011-01-26T19:01:12Z\",\n    \"updated_at\": \"2011-01-26T19:14:43Z\",\n    \"permissions\": {\n      \"admin\": false,\n      \"push\": false,\n      \"pull\": true\n    },\n    \"allow_rebase_merge\": true,\n    \"temp_clone_token\": \"ABTLWHOULUVAXGTRYU7OC2876QJ2O\",\n    \"allow_squash_merge\": true,\n    \"delete_branch_on_merge\": true,\n    \"allow_merge_commit\": true,\n    \"subscribers_count\": 42,\n    \"network_count\": 0,\n    \"license\": {\n      \"key\": \"mit\",\n      \"name\": \"MIT License\",\n      \"url\": \"https://api.github.com/licenses/mit\",\n      \"spdx_id\": \"MIT\",\n      \"node_id\": \"MDc6TGljZW5zZW1pdA==\",\n      \"html_url\": \"https://api.github.com/licenses/mit\"\n    },\n    \"forks\": 1,\n    \"open_issues\": 1,\n    \"watchers\": 1\n  },\n  \"source\": {\n    \"id\": 1296269,\n    \"node_id\": \"MDEwOlJlcG9zaXRvcnkxMjk2MjY5\",\n    \"name\": \"Hello-World\",\n    \"full_name\": \"octocat/Hello-World\",\n    \"owner\": {\n      \"login\": \"octocat\",\n      \"id\": 1,\n      \"node_id\": \"MDQ6VXNlcjE=\",\n      \"avatar_url\": \"https://github.com/images/error/octocat_happy.gif\",\n      \"gravatar_id\": \"\",\n      \"url\": \"https://api.github.com/users/octocat\",\n      \"html_url\": \"https://github.com/octocat\",\n      \"followers_url\": \"https://api.github.com/users/octocat/followers\",\n      \"following_url\": \"https://api.github.com/users/octocat/following{/other_user}\",\n      \"gists_url\": \"https://api.github.com/users/octocat/gists{/gist_id}\",\n      \"starred_url\": \"https://api.github.com/users/octocat/starred{/owner}{/repo}\",\n      \"subscriptions_url\": \"https://api.github.com/users/octocat/subscriptions\",\n      \"organizations_url\": \"https://api.github.com/users/octocat/orgs\",\n      \"repos_url\": \"https://api.github.com/users/octocat/repos\",\n      \"events_url\": \"https://api.github.com/users/octocat/events{/privacy}\",\n      \"received_events_url\": \"https://api.github.com/users/octocat/received_events\",\n      \"type\": \"User\",\n      \"site_admin\": false\n    },\n    \"private\": false,\n    \"html_url\": \"https://github.com/octocat/Hello-World\",\n    \"description\": \"This your first repo!\",\n    \"fork\": false,\n    \"url\": \"https://api.github.com/repos/octocat/Hello-World\",\n    \"archive_url\": \"https://api.github.com/repos/octocat/Hello-World/{archive_format}{/ref}\",\n    \"assignees_url\": \"https://api.github.com/repos/octocat/Hello-World/assignees{/user}\",\n    \"blobs_url\": \"https://api.github.com/repos/octocat/Hello-World/git/blobs{/sha}\",\n    \"branches_url\": \"https://api.github.com/repos/octocat/Hello-World/branches{/branch}\",\n    \"collaborators_url\": \"https://api.github.com/repos/octocat/Hello-World/collaborators{/collaborator}\",\n    \"comments_url\": \"https://api.github.com/repos/octocat/Hello-World/comments{/number}\",\n    \"commits_url\": \"https://api.github.com/repos/octocat/Hello-World/commits{/sha}\",\n    \"compare_url\": \"https://api.github.com/repos/octocat/Hello-World/compare/{base}...{head}\",\n    \"contents_url\": \"https://api.github.com/repos/octocat/Hello-World/contents/{+path}\",\n    \"contributors_url\": \"https://api.github.com/repos/octocat/Hello-World/contributors\",\n    \"deployments_url\": \"https://api.github.com/repos/octocat/Hello-World/deployments\",\n    \"downloads_url\": \"https://api.github.com/repos/octocat/Hello-World/downloads\",\n    \"events_url\": \"https://api.github.com/repos/octocat/Hello-World/events\",\n    \"forks_url\": \"https://api.github.com/repos/octocat/Hello-World/forks\",\n    \"git_commits_url\": \"https://api.github.com/repos/octocat/Hello-World/git/commits{/sha}\",\n    \"git_refs_url\": \"https://api.github.com/repos/octocat/Hello-World/git/refs{/sha}\",\n    \"git_tags_url\": \"https://api.github.com/repos/octocat/Hello-World/git/tags{/sha}\",\n    \"git_url\": \"git:github.com/octocat/Hello-World.git\",\n    \"issue_comment_url\": \"https://api.github.com/repos/octocat/Hello-World/issues/comments{/number}\",\n    \"issue_events_url\": \"https://api.github.com/repos/octocat/Hello-World/issues/events{/number}\",\n    \"issues_url\": \"https://api.github.com/repos/octocat/Hello-World/issues{/number}\",\n    \"keys_url\": \"https://api.github.com/repos/octocat/Hello-World/keys{/key_id}\",\n    \"labels_url\": \"https://api.github.com/repos/octocat/Hello-World/labels{/name}\",\n    \"languages_url\": \"https://api.github.com/repos/octocat/Hello-World/languages\",\n    \"merges_url\": \"https://api.github.com/repos/octocat/Hello-World/merges\",\n    \"milestones_url\": \"https://api.github.com/repos/octocat/Hello-World/milestones{/number}\",\n    \"notifications_url\": \"https://api.github.com/repos/octocat/Hello-World/notifications{?since,all,participating}\",\n    \"pulls_url\": \"https://api.github.com/repos/octocat/Hello-World/pulls{/number}\",\n    \"releases_url\": \"https://api.github.com/repos/octocat/Hello-World/releases{/id}\",\n    \"ssh_url\": \"git@github.com:octocat/Hello-World.git\",\n    \"stargazers_url\": \"https://api.github.com/repos/octocat/Hello-World/stargazers\",\n    \"statuses_url\": \"https://api.github.com/repos/octocat/Hello-World/statuses/{sha}\",\n    \"subscribers_url\": \"https://api.github.com/repos/octocat/Hello-World/subscribers\",\n    \"subscription_url\": \"https://api.github.com/repos/octocat/Hello-World/subscription\",\n    \"tags_url\": \"https://api.github.com/repos/octocat/Hello-World/tags\",\n    \"teams_url\": \"https://api.github.com/repos/octocat/Hello-World/teams\",\n    \"trees_url\": \"https://api.github.com/repos/octocat/Hello-World/git/trees{/sha}\",\n    \"clone_url\": \"https://github.com/octocat/Hello-World.git\",\n    \"mirror_url\": \"git:git.example.com/octocat/Hello-World\",\n    \"hooks_url\": \"https://api.github.com/repos/octocat/Hello-World/hooks\",\n    \"svn_url\": \"https://svn.github.com/octocat/Hello-World\",\n    \"homepage\": \"https://github.com\",\n    \"language\": null,\n    \"forks_count\": 9,\n    \"stargazers_count\": 80,\n    \"watchers_count\": 80,\n    \"size\": 108,\n    \"default_branch\": \"master\",\n    \"open_issues_count\": 0,\n    \"is_template\": true,\n    \"topics\": [\n      \"octocat\",\n      \"atom\",\n      \"electron\",\n      \"api\"\n    ],\n    \"has_issues\": true,\n    \"has_projects\": true,\n    \"has_wiki\": true,\n    \"has_pages\": false,\n    \"has_downloads\": true,\n    \"archived\": false,\n    \"disabled\": false,\n    \"visibility\": \"public\",\n    \"pushed_at\": \"2011-01-26T19:06:43Z\",\n    \"created_at\": \"2011-01-26T19:01:12Z\",\n    \"updated_at\": \"2011-01-26T19:14:43Z\",\n    \"permissions\": {\n      \"admin\": false,\n      \"push\": false,\n      \"pull\": true\n    },\n    \"allow_rebase_merge\": true,\n    \"temp_clone_token\": \"ABTLWHOULUVAXGTRYU7OC2876QJ2O\",\n    \"allow_squash_merge\": true,\n    \"delete_branch_on_merge\": true,\n    \"allow_merge_commit\": true,\n    \"subscribers_count\": 42,\n    \"network_count\": 0,\n    \"license\": {\n      \"key\": \"mit\",\n      \"name\": \"MIT License\",\n      \"url\": \"https://api.github.com/licenses/mit\",\n      \"spdx_id\": \"MIT\",\n      \"node_id\": \"MDc6TGljZW5zZW1pdA==\",\n      \"html_url\": \"https://api.github.com/licenses/mit\"\n    },\n    \"forks\": 1,\n    \"open_issues\": 1,\n    \"watchers\": 1\n  }\n}\n
" + }, + { + "httpStatusCode": "307", + "httpStatusMessage": "Temporary Redirect", + "description": "Temporary Redirect" + }, + { + "httpStatusCode": "403", + "httpStatusMessage": "Forbidden", + "description": "Forbidden" + }, + { + "httpStatusCode": "404", + "httpStatusMessage": "Not Found", + "description": "Resource not found" + }, + { + "httpStatusCode": "422", + "httpStatusMessage": "Unprocessable Entity", + "description": "Validation failed" + } + ], "bodyParameters": [ { "type": "string", @@ -31779,6 +31917,116 @@ "rawDescription": "Can be `public` or `private`. If your organization is associated with an enterprise account using GitHub Enterprise Cloud or GitHub Enterprise Server 2.20+, `visibility` can also be `internal`. The `visibility` parameter overrides the `private` parameter when you use both along with the `nebula-preview` preview header.", "childParamsGroups": [] }, + { + "type": "object or nullable", + "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.\" 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.

", + "name": "status", + "in": "body", + "rawType": "string", + "rawDescription": "Can be `enabled` or `disabled`.", + "childParamsGroups": [] + } + }, + "name": "advanced_security", + "in": "body", + "rawType": "object", + "rawDescription": "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.", + "childParamsGroups": [ + { + "parentName": "advanced_security", + "parentType": "object", + "id": "advanced_security-object", + "params": [ + { + "type": "string", + "description": "

Can be enabled or disabled.

", + "name": "status", + "in": "body", + "rawType": "string", + "rawDescription": "Can be `enabled` or `disabled`.", + "childParamsGroups": [] + } + ] + } + ] + } + }, + "name": "security_and_analysis", + "in": "body", + "rawType": "object", + "rawDescription": "Specify which security and analysis features to enable or disable.", + "childParamsGroups": [ + { + "parentName": "security_and_analysis", + "parentType": "object", + "id": "security_and_analysis-object", + "params": [ + { + "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.\" 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.

", + "name": "status", + "in": "body", + "rawType": "string", + "rawDescription": "Can be `enabled` or `disabled`.", + "childParamsGroups": [] + } + }, + "name": "advanced_security", + "in": "body", + "rawType": "object", + "rawDescription": "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.", + "childParamsGroups": [ + { + "parentName": "advanced_security", + "parentType": "object", + "id": "advanced_security-object", + "params": [ + { + "type": "string", + "description": "

Can be enabled or disabled.

", + "name": "status", + "in": "body", + "rawType": "string", + "rawDescription": "Can be `enabled` or `disabled`.", + "childParamsGroups": [] + } + ] + } + ] + } + ] + }, + { + "parentName": "advanced_security", + "parentType": "object", + "id": "advanced_security-object", + "params": [ + { + "type": "string", + "description": "

Can be enabled or disabled.

", + "name": "status", + "in": "body", + "rawType": "string", + "rawDescription": "Can be `enabled` or `disabled`.", + "childParamsGroups": [] + } + ] + } + ] + }, { "type": "boolean", "description": "

Either true to enable issues for this repository or false to disable them.

", @@ -31878,34 +32126,6 @@ "rawDescription": "`true` to archive this repository. **Note**: You cannot unarchive repositories through the API.", "childParamsGroups": [] } - ], - "responses": [ - { - "httpStatusCode": "200", - "httpStatusMessage": "OK", - "description": "Response", - "payload": "
{\n  \"id\": 1296269,\n  \"node_id\": \"MDEwOlJlcG9zaXRvcnkxMjk2MjY5\",\n  \"name\": \"Hello-World\",\n  \"full_name\": \"octocat/Hello-World\",\n  \"owner\": {\n    \"login\": \"octocat\",\n    \"id\": 1,\n    \"node_id\": \"MDQ6VXNlcjE=\",\n    \"avatar_url\": \"https://github.com/images/error/octocat_happy.gif\",\n    \"gravatar_id\": \"\",\n    \"url\": \"https://api.github.com/users/octocat\",\n    \"html_url\": \"https://github.com/octocat\",\n    \"followers_url\": \"https://api.github.com/users/octocat/followers\",\n    \"following_url\": \"https://api.github.com/users/octocat/following{/other_user}\",\n    \"gists_url\": \"https://api.github.com/users/octocat/gists{/gist_id}\",\n    \"starred_url\": \"https://api.github.com/users/octocat/starred{/owner}{/repo}\",\n    \"subscriptions_url\": \"https://api.github.com/users/octocat/subscriptions\",\n    \"organizations_url\": \"https://api.github.com/users/octocat/orgs\",\n    \"repos_url\": \"https://api.github.com/users/octocat/repos\",\n    \"events_url\": \"https://api.github.com/users/octocat/events{/privacy}\",\n    \"received_events_url\": \"https://api.github.com/users/octocat/received_events\",\n    \"type\": \"User\",\n    \"site_admin\": false\n  },\n  \"private\": false,\n  \"html_url\": \"https://github.com/octocat/Hello-World\",\n  \"description\": \"This your first repo!\",\n  \"fork\": false,\n  \"url\": \"https://api.github.com/repos/octocat/Hello-World\",\n  \"archive_url\": \"https://api.github.com/repos/octocat/Hello-World/{archive_format}{/ref}\",\n  \"assignees_url\": \"https://api.github.com/repos/octocat/Hello-World/assignees{/user}\",\n  \"blobs_url\": \"https://api.github.com/repos/octocat/Hello-World/git/blobs{/sha}\",\n  \"branches_url\": \"https://api.github.com/repos/octocat/Hello-World/branches{/branch}\",\n  \"collaborators_url\": \"https://api.github.com/repos/octocat/Hello-World/collaborators{/collaborator}\",\n  \"comments_url\": \"https://api.github.com/repos/octocat/Hello-World/comments{/number}\",\n  \"commits_url\": \"https://api.github.com/repos/octocat/Hello-World/commits{/sha}\",\n  \"compare_url\": \"https://api.github.com/repos/octocat/Hello-World/compare/{base}...{head}\",\n  \"contents_url\": \"https://api.github.com/repos/octocat/Hello-World/contents/{+path}\",\n  \"contributors_url\": \"https://api.github.com/repos/octocat/Hello-World/contributors\",\n  \"deployments_url\": \"https://api.github.com/repos/octocat/Hello-World/deployments\",\n  \"downloads_url\": \"https://api.github.com/repos/octocat/Hello-World/downloads\",\n  \"events_url\": \"https://api.github.com/repos/octocat/Hello-World/events\",\n  \"forks_url\": \"https://api.github.com/repos/octocat/Hello-World/forks\",\n  \"git_commits_url\": \"https://api.github.com/repos/octocat/Hello-World/git/commits{/sha}\",\n  \"git_refs_url\": \"https://api.github.com/repos/octocat/Hello-World/git/refs{/sha}\",\n  \"git_tags_url\": \"https://api.github.com/repos/octocat/Hello-World/git/tags{/sha}\",\n  \"git_url\": \"git:github.com/octocat/Hello-World.git\",\n  \"issue_comment_url\": \"https://api.github.com/repos/octocat/Hello-World/issues/comments{/number}\",\n  \"issue_events_url\": \"https://api.github.com/repos/octocat/Hello-World/issues/events{/number}\",\n  \"issues_url\": \"https://api.github.com/repos/octocat/Hello-World/issues{/number}\",\n  \"keys_url\": \"https://api.github.com/repos/octocat/Hello-World/keys{/key_id}\",\n  \"labels_url\": \"https://api.github.com/repos/octocat/Hello-World/labels{/name}\",\n  \"languages_url\": \"https://api.github.com/repos/octocat/Hello-World/languages\",\n  \"merges_url\": \"https://api.github.com/repos/octocat/Hello-World/merges\",\n  \"milestones_url\": \"https://api.github.com/repos/octocat/Hello-World/milestones{/number}\",\n  \"notifications_url\": \"https://api.github.com/repos/octocat/Hello-World/notifications{?since,all,participating}\",\n  \"pulls_url\": \"https://api.github.com/repos/octocat/Hello-World/pulls{/number}\",\n  \"releases_url\": \"https://api.github.com/repos/octocat/Hello-World/releases{/id}\",\n  \"ssh_url\": \"git@github.com:octocat/Hello-World.git\",\n  \"stargazers_url\": \"https://api.github.com/repos/octocat/Hello-World/stargazers\",\n  \"statuses_url\": \"https://api.github.com/repos/octocat/Hello-World/statuses/{sha}\",\n  \"subscribers_url\": \"https://api.github.com/repos/octocat/Hello-World/subscribers\",\n  \"subscription_url\": \"https://api.github.com/repos/octocat/Hello-World/subscription\",\n  \"tags_url\": \"https://api.github.com/repos/octocat/Hello-World/tags\",\n  \"teams_url\": \"https://api.github.com/repos/octocat/Hello-World/teams\",\n  \"trees_url\": \"https://api.github.com/repos/octocat/Hello-World/git/trees{/sha}\",\n  \"clone_url\": \"https://github.com/octocat/Hello-World.git\",\n  \"mirror_url\": \"git:git.example.com/octocat/Hello-World\",\n  \"hooks_url\": \"https://api.github.com/repos/octocat/Hello-World/hooks\",\n  \"svn_url\": \"https://svn.github.com/octocat/Hello-World\",\n  \"homepage\": \"https://github.com\",\n  \"license\": {\n    \"key\": \"mit\",\n    \"name\": \"MIT License\",\n    \"url\": \"https://api.github.com/licenses/mit\",\n    \"spdx_id\": \"MIT\",\n    \"node_id\": \"MDc6TGljZW5zZW1pdA==\",\n    \"html_url\": \"https://github.com/licenses/mit\"\n  },\n  \"language\": null,\n  \"forks_count\": 9,\n  \"forks\": 9,\n  \"stargazers_count\": 80,\n  \"watchers_count\": 80,\n  \"watchers\": 80,\n  \"size\": 108,\n  \"default_branch\": \"master\",\n  \"open_issues_count\": 0,\n  \"open_issues\": 0,\n  \"is_template\": false,\n  \"topics\": [\n    \"octocat\",\n    \"atom\",\n    \"electron\",\n    \"api\"\n  ],\n  \"has_issues\": true,\n  \"has_projects\": true,\n  \"has_wiki\": true,\n  \"has_pages\": false,\n  \"has_downloads\": true,\n  \"archived\": false,\n  \"disabled\": false,\n  \"visibility\": \"public\",\n  \"pushed_at\": \"2011-01-26T19:06:43Z\",\n  \"created_at\": \"2011-01-26T19:01:12Z\",\n  \"updated_at\": \"2011-01-26T19:14:43Z\",\n  \"permissions\": {\n    \"pull\": true,\n    \"push\": false,\n    \"admin\": false\n  },\n  \"allow_rebase_merge\": true,\n  \"template_repository\": {\n    \"id\": 1296269,\n    \"node_id\": \"MDEwOlJlcG9zaXRvcnkxMjk2MjY5\",\n    \"name\": \"Hello-World-Template\",\n    \"full_name\": \"octocat/Hello-World-Template\",\n    \"owner\": {\n      \"login\": \"octocat\",\n      \"id\": 1,\n      \"node_id\": \"MDQ6VXNlcjE=\",\n      \"avatar_url\": \"https://github.com/images/error/octocat_happy.gif\",\n      \"gravatar_id\": \"\",\n      \"url\": \"https://api.github.com/users/octocat\",\n      \"html_url\": \"https://github.com/octocat\",\n      \"followers_url\": \"https://api.github.com/users/octocat/followers\",\n      \"following_url\": \"https://api.github.com/users/octocat/following{/other_user}\",\n      \"gists_url\": \"https://api.github.com/users/octocat/gists{/gist_id}\",\n      \"starred_url\": \"https://api.github.com/users/octocat/starred{/owner}{/repo}\",\n      \"subscriptions_url\": \"https://api.github.com/users/octocat/subscriptions\",\n      \"organizations_url\": \"https://api.github.com/users/octocat/orgs\",\n      \"repos_url\": \"https://api.github.com/users/octocat/repos\",\n      \"events_url\": \"https://api.github.com/users/octocat/events{/privacy}\",\n      \"received_events_url\": \"https://api.github.com/users/octocat/received_events\",\n      \"type\": \"User\",\n      \"site_admin\": false\n    },\n    \"private\": false,\n    \"html_url\": \"https://github.com/octocat/Hello-World-Template\",\n    \"description\": \"This your first repo!\",\n    \"fork\": false,\n    \"url\": \"https://api.github.com/repos/octocat/Hello-World-Template\",\n    \"archive_url\": \"https://api.github.com/repos/octocat/Hello-World-Template/{archive_format}{/ref}\",\n    \"assignees_url\": \"https://api.github.com/repos/octocat/Hello-World-Template/assignees{/user}\",\n    \"blobs_url\": \"https://api.github.com/repos/octocat/Hello-World-Template/git/blobs{/sha}\",\n    \"branches_url\": \"https://api.github.com/repos/octocat/Hello-World-Template/branches{/branch}\",\n    \"collaborators_url\": \"https://api.github.com/repos/octocat/Hello-World-Template/collaborators{/collaborator}\",\n    \"comments_url\": \"https://api.github.com/repos/octocat/Hello-World-Template/comments{/number}\",\n    \"commits_url\": \"https://api.github.com/repos/octocat/Hello-World-Template/commits{/sha}\",\n    \"compare_url\": \"https://api.github.com/repos/octocat/Hello-World-Template/compare/{base}...{head}\",\n    \"contents_url\": \"https://api.github.com/repos/octocat/Hello-World-Template/contents/{+path}\",\n    \"contributors_url\": \"https://api.github.com/repos/octocat/Hello-World-Template/contributors\",\n    \"deployments_url\": \"https://api.github.com/repos/octocat/Hello-World-Template/deployments\",\n    \"downloads_url\": \"https://api.github.com/repos/octocat/Hello-World-Template/downloads\",\n    \"events_url\": \"https://api.github.com/repos/octocat/Hello-World-Template/events\",\n    \"forks_url\": \"https://api.github.com/repos/octocat/Hello-World-Template/forks\",\n    \"git_commits_url\": \"https://api.github.com/repos/octocat/Hello-World-Template/git/commits{/sha}\",\n    \"git_refs_url\": \"https://api.github.com/repos/octocat/Hello-World-Template/git/refs{/sha}\",\n    \"git_tags_url\": \"https://api.github.com/repos/octocat/Hello-World-Template/git/tags{/sha}\",\n    \"git_url\": \"git:github.com/octocat/Hello-World-Template.git\",\n    \"issue_comment_url\": \"https://api.github.com/repos/octocat/Hello-World-Template/issues/comments{/number}\",\n    \"issue_events_url\": \"https://api.github.com/repos/octocat/Hello-World-Template/issues/events{/number}\",\n    \"issues_url\": \"https://api.github.com/repos/octocat/Hello-World-Template/issues{/number}\",\n    \"keys_url\": \"https://api.github.com/repos/octocat/Hello-World-Template/keys{/key_id}\",\n    \"labels_url\": \"https://api.github.com/repos/octocat/Hello-World-Template/labels{/name}\",\n    \"languages_url\": \"https://api.github.com/repos/octocat/Hello-World-Template/languages\",\n    \"merges_url\": \"https://api.github.com/repos/octocat/Hello-World-Template/merges\",\n    \"milestones_url\": \"https://api.github.com/repos/octocat/Hello-World-Template/milestones{/number}\",\n    \"notifications_url\": \"https://api.github.com/repos/octocat/Hello-World-Template/notifications{?since,all,participating}\",\n    \"pulls_url\": \"https://api.github.com/repos/octocat/Hello-World-Template/pulls{/number}\",\n    \"releases_url\": \"https://api.github.com/repos/octocat/Hello-World-Template/releases{/id}\",\n    \"ssh_url\": \"git@github.com:octocat/Hello-World-Template.git\",\n    \"stargazers_url\": \"https://api.github.com/repos/octocat/Hello-World-Template/stargazers\",\n    \"statuses_url\": \"https://api.github.com/repos/octocat/Hello-World-Template/statuses/{sha}\",\n    \"subscribers_url\": \"https://api.github.com/repos/octocat/Hello-World-Template/subscribers\",\n    \"subscription_url\": \"https://api.github.com/repos/octocat/Hello-World-Template/subscription\",\n    \"tags_url\": \"https://api.github.com/repos/octocat/Hello-World-Template/tags\",\n    \"teams_url\": \"https://api.github.com/repos/octocat/Hello-World-Template/teams\",\n    \"trees_url\": \"https://api.github.com/repos/octocat/Hello-World-Template/git/trees{/sha}\",\n    \"clone_url\": \"https://github.com/octocat/Hello-World-Template.git\",\n    \"mirror_url\": \"git:git.example.com/octocat/Hello-World-Template\",\n    \"hooks_url\": \"https://api.github.com/repos/octocat/Hello-World-Template/hooks\",\n    \"svn_url\": \"https://svn.github.com/octocat/Hello-World-Template\",\n    \"homepage\": \"https://github.com\",\n    \"language\": null,\n    \"forks\": 9,\n    \"forks_count\": 9,\n    \"stargazers_count\": 80,\n    \"watchers_count\": 80,\n    \"watchers\": 80,\n    \"size\": 108,\n    \"default_branch\": \"master\",\n    \"open_issues\": 0,\n    \"open_issues_count\": 0,\n    \"is_template\": true,\n    \"license\": {\n      \"key\": \"mit\",\n      \"name\": \"MIT License\",\n      \"url\": \"https://api.github.com/licenses/mit\",\n      \"spdx_id\": \"MIT\",\n      \"node_id\": \"MDc6TGljZW5zZW1pdA==\",\n      \"html_url\": \"https://api.github.com/licenses/mit\"\n    },\n    \"topics\": [\n      \"octocat\",\n      \"atom\",\n      \"electron\",\n      \"api\"\n    ],\n    \"has_issues\": true,\n    \"has_projects\": true,\n    \"has_wiki\": true,\n    \"has_pages\": false,\n    \"has_downloads\": true,\n    \"archived\": false,\n    \"disabled\": false,\n    \"visibility\": \"public\",\n    \"pushed_at\": \"2011-01-26T19:06:43Z\",\n    \"created_at\": \"2011-01-26T19:01:12Z\",\n    \"updated_at\": \"2011-01-26T19:14:43Z\",\n    \"permissions\": {\n      \"admin\": false,\n      \"push\": false,\n      \"pull\": true\n    },\n    \"allow_rebase_merge\": true,\n    \"temp_clone_token\": \"ABTLWHOULUVAXGTRYU7OC2876QJ2O\",\n    \"allow_squash_merge\": true,\n    \"delete_branch_on_merge\": true,\n    \"allow_merge_commit\": true,\n    \"subscribers_count\": 42,\n    \"network_count\": 0\n  },\n  \"temp_clone_token\": \"ABTLWHOULUVAXGTRYU7OC2876QJ2O\",\n  \"allow_squash_merge\": true,\n  \"delete_branch_on_merge\": true,\n  \"allow_merge_commit\": true,\n  \"subscribers_count\": 42,\n  \"network_count\": 0,\n  \"organization\": {\n    \"login\": \"octocat\",\n    \"id\": 1,\n    \"node_id\": \"MDQ6VXNlcjE=\",\n    \"avatar_url\": \"https://github.com/images/error/octocat_happy.gif\",\n    \"gravatar_id\": \"\",\n    \"url\": \"https://api.github.com/users/octocat\",\n    \"html_url\": \"https://github.com/octocat\",\n    \"followers_url\": \"https://api.github.com/users/octocat/followers\",\n    \"following_url\": \"https://api.github.com/users/octocat/following{/other_user}\",\n    \"gists_url\": \"https://api.github.com/users/octocat/gists{/gist_id}\",\n    \"starred_url\": \"https://api.github.com/users/octocat/starred{/owner}{/repo}\",\n    \"subscriptions_url\": \"https://api.github.com/users/octocat/subscriptions\",\n    \"organizations_url\": \"https://api.github.com/users/octocat/orgs\",\n    \"repos_url\": \"https://api.github.com/users/octocat/repos\",\n    \"events_url\": \"https://api.github.com/users/octocat/events{/privacy}\",\n    \"received_events_url\": \"https://api.github.com/users/octocat/received_events\",\n    \"type\": \"Organization\",\n    \"site_admin\": false\n  },\n  \"parent\": {\n    \"id\": 1296269,\n    \"node_id\": \"MDEwOlJlcG9zaXRvcnkxMjk2MjY5\",\n    \"name\": \"Hello-World\",\n    \"full_name\": \"octocat/Hello-World\",\n    \"owner\": {\n      \"login\": \"octocat\",\n      \"id\": 1,\n      \"node_id\": \"MDQ6VXNlcjE=\",\n      \"avatar_url\": \"https://github.com/images/error/octocat_happy.gif\",\n      \"gravatar_id\": \"\",\n      \"url\": \"https://api.github.com/users/octocat\",\n      \"html_url\": \"https://github.com/octocat\",\n      \"followers_url\": \"https://api.github.com/users/octocat/followers\",\n      \"following_url\": \"https://api.github.com/users/octocat/following{/other_user}\",\n      \"gists_url\": \"https://api.github.com/users/octocat/gists{/gist_id}\",\n      \"starred_url\": \"https://api.github.com/users/octocat/starred{/owner}{/repo}\",\n      \"subscriptions_url\": \"https://api.github.com/users/octocat/subscriptions\",\n      \"organizations_url\": \"https://api.github.com/users/octocat/orgs\",\n      \"repos_url\": \"https://api.github.com/users/octocat/repos\",\n      \"events_url\": \"https://api.github.com/users/octocat/events{/privacy}\",\n      \"received_events_url\": \"https://api.github.com/users/octocat/received_events\",\n      \"type\": \"User\",\n      \"site_admin\": false\n    },\n    \"private\": false,\n    \"html_url\": \"https://github.com/octocat/Hello-World\",\n    \"description\": \"This your first repo!\",\n    \"fork\": false,\n    \"url\": \"https://api.github.com/repos/octocat/Hello-World\",\n    \"archive_url\": \"https://api.github.com/repos/octocat/Hello-World/{archive_format}{/ref}\",\n    \"assignees_url\": \"https://api.github.com/repos/octocat/Hello-World/assignees{/user}\",\n    \"blobs_url\": \"https://api.github.com/repos/octocat/Hello-World/git/blobs{/sha}\",\n    \"branches_url\": \"https://api.github.com/repos/octocat/Hello-World/branches{/branch}\",\n    \"collaborators_url\": \"https://api.github.com/repos/octocat/Hello-World/collaborators{/collaborator}\",\n    \"comments_url\": \"https://api.github.com/repos/octocat/Hello-World/comments{/number}\",\n    \"commits_url\": \"https://api.github.com/repos/octocat/Hello-World/commits{/sha}\",\n    \"compare_url\": \"https://api.github.com/repos/octocat/Hello-World/compare/{base}...{head}\",\n    \"contents_url\": \"https://api.github.com/repos/octocat/Hello-World/contents/{+path}\",\n    \"contributors_url\": \"https://api.github.com/repos/octocat/Hello-World/contributors\",\n    \"deployments_url\": \"https://api.github.com/repos/octocat/Hello-World/deployments\",\n    \"downloads_url\": \"https://api.github.com/repos/octocat/Hello-World/downloads\",\n    \"events_url\": \"https://api.github.com/repos/octocat/Hello-World/events\",\n    \"forks_url\": \"https://api.github.com/repos/octocat/Hello-World/forks\",\n    \"git_commits_url\": \"https://api.github.com/repos/octocat/Hello-World/git/commits{/sha}\",\n    \"git_refs_url\": \"https://api.github.com/repos/octocat/Hello-World/git/refs{/sha}\",\n    \"git_tags_url\": \"https://api.github.com/repos/octocat/Hello-World/git/tags{/sha}\",\n    \"git_url\": \"git:github.com/octocat/Hello-World.git\",\n    \"issue_comment_url\": \"https://api.github.com/repos/octocat/Hello-World/issues/comments{/number}\",\n    \"issue_events_url\": \"https://api.github.com/repos/octocat/Hello-World/issues/events{/number}\",\n    \"issues_url\": \"https://api.github.com/repos/octocat/Hello-World/issues{/number}\",\n    \"keys_url\": \"https://api.github.com/repos/octocat/Hello-World/keys{/key_id}\",\n    \"labels_url\": \"https://api.github.com/repos/octocat/Hello-World/labels{/name}\",\n    \"languages_url\": \"https://api.github.com/repos/octocat/Hello-World/languages\",\n    \"merges_url\": \"https://api.github.com/repos/octocat/Hello-World/merges\",\n    \"milestones_url\": \"https://api.github.com/repos/octocat/Hello-World/milestones{/number}\",\n    \"notifications_url\": \"https://api.github.com/repos/octocat/Hello-World/notifications{?since,all,participating}\",\n    \"pulls_url\": \"https://api.github.com/repos/octocat/Hello-World/pulls{/number}\",\n    \"releases_url\": \"https://api.github.com/repos/octocat/Hello-World/releases{/id}\",\n    \"ssh_url\": \"git@github.com:octocat/Hello-World.git\",\n    \"stargazers_url\": \"https://api.github.com/repos/octocat/Hello-World/stargazers\",\n    \"statuses_url\": \"https://api.github.com/repos/octocat/Hello-World/statuses/{sha}\",\n    \"subscribers_url\": \"https://api.github.com/repos/octocat/Hello-World/subscribers\",\n    \"subscription_url\": \"https://api.github.com/repos/octocat/Hello-World/subscription\",\n    \"tags_url\": \"https://api.github.com/repos/octocat/Hello-World/tags\",\n    \"teams_url\": \"https://api.github.com/repos/octocat/Hello-World/teams\",\n    \"trees_url\": \"https://api.github.com/repos/octocat/Hello-World/git/trees{/sha}\",\n    \"clone_url\": \"https://github.com/octocat/Hello-World.git\",\n    \"mirror_url\": \"git:git.example.com/octocat/Hello-World\",\n    \"hooks_url\": \"https://api.github.com/repos/octocat/Hello-World/hooks\",\n    \"svn_url\": \"https://svn.github.com/octocat/Hello-World\",\n    \"homepage\": \"https://github.com\",\n    \"language\": null,\n    \"forks_count\": 9,\n    \"stargazers_count\": 80,\n    \"watchers_count\": 80,\n    \"size\": 108,\n    \"default_branch\": \"master\",\n    \"open_issues_count\": 0,\n    \"is_template\": true,\n    \"topics\": [\n      \"octocat\",\n      \"atom\",\n      \"electron\",\n      \"api\"\n    ],\n    \"has_issues\": true,\n    \"has_projects\": true,\n    \"has_wiki\": true,\n    \"has_pages\": false,\n    \"has_downloads\": true,\n    \"archived\": false,\n    \"disabled\": false,\n    \"visibility\": \"public\",\n    \"pushed_at\": \"2011-01-26T19:06:43Z\",\n    \"created_at\": \"2011-01-26T19:01:12Z\",\n    \"updated_at\": \"2011-01-26T19:14:43Z\",\n    \"permissions\": {\n      \"admin\": false,\n      \"push\": false,\n      \"pull\": true\n    },\n    \"allow_rebase_merge\": true,\n    \"temp_clone_token\": \"ABTLWHOULUVAXGTRYU7OC2876QJ2O\",\n    \"allow_squash_merge\": true,\n    \"delete_branch_on_merge\": true,\n    \"allow_merge_commit\": true,\n    \"subscribers_count\": 42,\n    \"network_count\": 0,\n    \"license\": {\n      \"key\": \"mit\",\n      \"name\": \"MIT License\",\n      \"url\": \"https://api.github.com/licenses/mit\",\n      \"spdx_id\": \"MIT\",\n      \"node_id\": \"MDc6TGljZW5zZW1pdA==\",\n      \"html_url\": \"https://api.github.com/licenses/mit\"\n    },\n    \"forks\": 1,\n    \"open_issues\": 1,\n    \"watchers\": 1\n  },\n  \"source\": {\n    \"id\": 1296269,\n    \"node_id\": \"MDEwOlJlcG9zaXRvcnkxMjk2MjY5\",\n    \"name\": \"Hello-World\",\n    \"full_name\": \"octocat/Hello-World\",\n    \"owner\": {\n      \"login\": \"octocat\",\n      \"id\": 1,\n      \"node_id\": \"MDQ6VXNlcjE=\",\n      \"avatar_url\": \"https://github.com/images/error/octocat_happy.gif\",\n      \"gravatar_id\": \"\",\n      \"url\": \"https://api.github.com/users/octocat\",\n      \"html_url\": \"https://github.com/octocat\",\n      \"followers_url\": \"https://api.github.com/users/octocat/followers\",\n      \"following_url\": \"https://api.github.com/users/octocat/following{/other_user}\",\n      \"gists_url\": \"https://api.github.com/users/octocat/gists{/gist_id}\",\n      \"starred_url\": \"https://api.github.com/users/octocat/starred{/owner}{/repo}\",\n      \"subscriptions_url\": \"https://api.github.com/users/octocat/subscriptions\",\n      \"organizations_url\": \"https://api.github.com/users/octocat/orgs\",\n      \"repos_url\": \"https://api.github.com/users/octocat/repos\",\n      \"events_url\": \"https://api.github.com/users/octocat/events{/privacy}\",\n      \"received_events_url\": \"https://api.github.com/users/octocat/received_events\",\n      \"type\": \"User\",\n      \"site_admin\": false\n    },\n    \"private\": false,\n    \"html_url\": \"https://github.com/octocat/Hello-World\",\n    \"description\": \"This your first repo!\",\n    \"fork\": false,\n    \"url\": \"https://api.github.com/repos/octocat/Hello-World\",\n    \"archive_url\": \"https://api.github.com/repos/octocat/Hello-World/{archive_format}{/ref}\",\n    \"assignees_url\": \"https://api.github.com/repos/octocat/Hello-World/assignees{/user}\",\n    \"blobs_url\": \"https://api.github.com/repos/octocat/Hello-World/git/blobs{/sha}\",\n    \"branches_url\": \"https://api.github.com/repos/octocat/Hello-World/branches{/branch}\",\n    \"collaborators_url\": \"https://api.github.com/repos/octocat/Hello-World/collaborators{/collaborator}\",\n    \"comments_url\": \"https://api.github.com/repos/octocat/Hello-World/comments{/number}\",\n    \"commits_url\": \"https://api.github.com/repos/octocat/Hello-World/commits{/sha}\",\n    \"compare_url\": \"https://api.github.com/repos/octocat/Hello-World/compare/{base}...{head}\",\n    \"contents_url\": \"https://api.github.com/repos/octocat/Hello-World/contents/{+path}\",\n    \"contributors_url\": \"https://api.github.com/repos/octocat/Hello-World/contributors\",\n    \"deployments_url\": \"https://api.github.com/repos/octocat/Hello-World/deployments\",\n    \"downloads_url\": \"https://api.github.com/repos/octocat/Hello-World/downloads\",\n    \"events_url\": \"https://api.github.com/repos/octocat/Hello-World/events\",\n    \"forks_url\": \"https://api.github.com/repos/octocat/Hello-World/forks\",\n    \"git_commits_url\": \"https://api.github.com/repos/octocat/Hello-World/git/commits{/sha}\",\n    \"git_refs_url\": \"https://api.github.com/repos/octocat/Hello-World/git/refs{/sha}\",\n    \"git_tags_url\": \"https://api.github.com/repos/octocat/Hello-World/git/tags{/sha}\",\n    \"git_url\": \"git:github.com/octocat/Hello-World.git\",\n    \"issue_comment_url\": \"https://api.github.com/repos/octocat/Hello-World/issues/comments{/number}\",\n    \"issue_events_url\": \"https://api.github.com/repos/octocat/Hello-World/issues/events{/number}\",\n    \"issues_url\": \"https://api.github.com/repos/octocat/Hello-World/issues{/number}\",\n    \"keys_url\": \"https://api.github.com/repos/octocat/Hello-World/keys{/key_id}\",\n    \"labels_url\": \"https://api.github.com/repos/octocat/Hello-World/labels{/name}\",\n    \"languages_url\": \"https://api.github.com/repos/octocat/Hello-World/languages\",\n    \"merges_url\": \"https://api.github.com/repos/octocat/Hello-World/merges\",\n    \"milestones_url\": \"https://api.github.com/repos/octocat/Hello-World/milestones{/number}\",\n    \"notifications_url\": \"https://api.github.com/repos/octocat/Hello-World/notifications{?since,all,participating}\",\n    \"pulls_url\": \"https://api.github.com/repos/octocat/Hello-World/pulls{/number}\",\n    \"releases_url\": \"https://api.github.com/repos/octocat/Hello-World/releases{/id}\",\n    \"ssh_url\": \"git@github.com:octocat/Hello-World.git\",\n    \"stargazers_url\": \"https://api.github.com/repos/octocat/Hello-World/stargazers\",\n    \"statuses_url\": \"https://api.github.com/repos/octocat/Hello-World/statuses/{sha}\",\n    \"subscribers_url\": \"https://api.github.com/repos/octocat/Hello-World/subscribers\",\n    \"subscription_url\": \"https://api.github.com/repos/octocat/Hello-World/subscription\",\n    \"tags_url\": \"https://api.github.com/repos/octocat/Hello-World/tags\",\n    \"teams_url\": \"https://api.github.com/repos/octocat/Hello-World/teams\",\n    \"trees_url\": \"https://api.github.com/repos/octocat/Hello-World/git/trees{/sha}\",\n    \"clone_url\": \"https://github.com/octocat/Hello-World.git\",\n    \"mirror_url\": \"git:git.example.com/octocat/Hello-World\",\n    \"hooks_url\": \"https://api.github.com/repos/octocat/Hello-World/hooks\",\n    \"svn_url\": \"https://svn.github.com/octocat/Hello-World\",\n    \"homepage\": \"https://github.com\",\n    \"language\": null,\n    \"forks_count\": 9,\n    \"stargazers_count\": 80,\n    \"watchers_count\": 80,\n    \"size\": 108,\n    \"default_branch\": \"master\",\n    \"open_issues_count\": 0,\n    \"is_template\": true,\n    \"topics\": [\n      \"octocat\",\n      \"atom\",\n      \"electron\",\n      \"api\"\n    ],\n    \"has_issues\": true,\n    \"has_projects\": true,\n    \"has_wiki\": true,\n    \"has_pages\": false,\n    \"has_downloads\": true,\n    \"archived\": false,\n    \"disabled\": false,\n    \"visibility\": \"public\",\n    \"pushed_at\": \"2011-01-26T19:06:43Z\",\n    \"created_at\": \"2011-01-26T19:01:12Z\",\n    \"updated_at\": \"2011-01-26T19:14:43Z\",\n    \"permissions\": {\n      \"admin\": false,\n      \"push\": false,\n      \"pull\": true\n    },\n    \"allow_rebase_merge\": true,\n    \"temp_clone_token\": \"ABTLWHOULUVAXGTRYU7OC2876QJ2O\",\n    \"allow_squash_merge\": true,\n    \"delete_branch_on_merge\": true,\n    \"allow_merge_commit\": true,\n    \"subscribers_count\": 42,\n    \"network_count\": 0,\n    \"license\": {\n      \"key\": \"mit\",\n      \"name\": \"MIT License\",\n      \"url\": \"https://api.github.com/licenses/mit\",\n      \"spdx_id\": \"MIT\",\n      \"node_id\": \"MDc6TGljZW5zZW1pdA==\",\n      \"html_url\": \"https://api.github.com/licenses/mit\"\n    },\n    \"forks\": 1,\n    \"open_issues\": 1,\n    \"watchers\": 1\n  }\n}\n
" - }, - { - "httpStatusCode": "307", - "httpStatusMessage": "Temporary Redirect", - "description": "Temporary Redirect" - }, - { - "httpStatusCode": "403", - "httpStatusMessage": "Forbidden", - "description": "Forbidden" - }, - { - "httpStatusCode": "404", - "httpStatusMessage": "Not Found", - "description": "Resource not found" - }, - { - "httpStatusCode": "422", - "httpStatusMessage": "Unprocessable Entity", - "description": "Validation failed" - } ] }, { @@ -86662,18 +86882,8 @@ "application/json": { "schema": { "properties": { - "email": { - "description": "

An email address associated with the GitHub user account to manage.

", - "type": "string", - "example": "org@example.com", - "name": "email", - "in": "body", - "rawType": "string", - "rawDescription": "An email address associated with the GitHub user account to manage.", - "childParamsGroups": [] - }, "visibility": { - "description": "

Required. Denotes whether an email is publically visible.

", + "description": "

Required. Denotes whether an email is publicly visible.

", "type": "string", "enum": [ "public", @@ -86682,7 +86892,7 @@ "name": "visibility", "in": "body", "rawType": "string", - "rawDescription": "Denotes whether an email is publically visible.", + "rawDescription": "Denotes whether an email is publicly visible.", "childParamsGroups": [] } }, @@ -86710,17 +86920,7 @@ "descriptionHTML": "

Sets the visibility for your primary email addresses.

", "bodyParameters": [ { - "description": "

An email address associated with the GitHub user account to manage.

", - "type": "string", - "example": "org@example.com", - "name": "email", - "in": "body", - "rawType": "string", - "rawDescription": "An email address associated with the GitHub user account to manage.", - "childParamsGroups": [] - }, - { - "description": "

Required. Denotes whether an email is publically visible.

", + "description": "

Required. Denotes whether an email is publicly visible.

", "type": "string", "enum": [ "public", @@ -86729,7 +86929,7 @@ "name": "visibility", "in": "body", "rawType": "string", - "rawDescription": "Denotes whether an email is publically visible.", + "rawDescription": "Denotes whether an email is publicly visible.", "childParamsGroups": [] } ], diff --git a/lib/rest/static/dereferenced/api.github.com.deref.json b/lib/rest/static/dereferenced/api.github.com.deref.json index 53e3faf3b2..e1a600ede0 100644 --- a/lib/rest/static/dereferenced/api.github.com.deref.json +++ b/lib/rest/static/dereferenced/api.github.com.deref.json @@ -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", diff --git a/lib/rest/static/dereferenced/github.ae.deref.json b/lib/rest/static/dereferenced/github.ae.deref.json index 1b591d4c76..118e528390 100644 --- a/lib/rest/static/dereferenced/github.ae.deref.json +++ b/lib/rest/static/dereferenced/github.ae.deref.json @@ -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", diff --git a/lib/rewrite-local-links.js b/lib/rewrite-local-links.js index 947c684684..d30101f28c 100644 --- a/lib/rewrite-local-links.js +++ b/lib/rewrite-local-links.js @@ -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 diff --git a/lib/search/algolia-search.js b/lib/search/algolia-search.js index 4f415a9c5c..4b915a52b9 100644 --- a/lib/search/algolia-search.js +++ b/lib/search/algolia-search.js @@ -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 diff --git a/lib/search/lunr-search.js b/lib/search/lunr-search.js index 5d01d60faa..8fddc5ff35 100644 --- a/lib/search/lunr-search.js +++ b/lib/search/lunr-search.js @@ -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' diff --git a/middleware/archived-enterprise-versions.js b/middleware/archived-enterprise-versions.js index 3a5cd96f7f..c599a49db1 100644 --- a/middleware/archived-enterprise-versions.js +++ b/middleware/archived-enterprise-versions.js @@ -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 diff --git a/middleware/context.js b/middleware/context.js index 42cfcfe168..9eedd09a98 100644 --- a/middleware/context.js +++ b/middleware/context.js @@ -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') diff --git a/middleware/contextualizers/features.js b/middleware/contextualizers/features.js new file mode 100644 index 0000000000..89e5dfb84c --- /dev/null +++ b/middleware/contextualizers/features.js @@ -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() +} diff --git a/middleware/contextualizers/graphql.js b/middleware/contextualizers/graphql.js index f07a1506ed..037f4c0a3b 100644 --- a/middleware/contextualizers/graphql.js +++ b/middleware/contextualizers/graphql.js @@ -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' diff --git a/middleware/contextualizers/webhooks.js b/middleware/contextualizers/webhooks.js index 884e8fd154..bdfaef423f 100644 --- a/middleware/contextualizers/webhooks.js +++ b/middleware/contextualizers/webhooks.js @@ -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') diff --git a/middleware/index.js b/middleware/index.js index 32b176592b..e6198c1826 100644 --- a/middleware/index.js +++ b/middleware/index.js @@ -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) diff --git a/middleware/redirects/external.js b/middleware/redirects/external.js index b6172696e3..71c8344ed2 100644 --- a/middleware/redirects/external.js +++ b/middleware/redirects/external.js @@ -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) { diff --git a/script/enterprise-server-deprecations/archive-version.js b/script/enterprise-server-deprecations/archive-version.js index c7395aee7d..8b40d5e2c4 100755 --- a/script/enterprise-server-deprecations/archive-version.js +++ b/script/enterprise-server-deprecations/archive-version.js @@ -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 => { diff --git a/script/enterprise-server-deprecations/remove-version-markup.js b/script/enterprise-server-deprecations/remove-version-markup.js index df8eda0973..a6c5a35f1c 100755 --- a/script/enterprise-server-deprecations/remove-version-markup.js +++ b/script/enterprise-server-deprecations/remove-version-markup.js @@ -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.`) diff --git a/script/enterprise-server-releases/create-graphql-files.js b/script/enterprise-server-releases/create-graphql-files.js index 42c969fe6c..547946088a 100755 --- a/script/enterprise-server-releases/create-graphql-files.js +++ b/script/enterprise-server-releases/create-graphql-files.js @@ -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') diff --git a/script/graphql/update-files.js b/script/graphql/update-files.js index 50da5fbbf7..408b45893b 100755 --- a/script/graphql/update-files.js +++ b/script/graphql/update-files.js @@ -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') diff --git a/script/graphql/utils/process-schemas.js b/script/graphql/utils/process-schemas.js index 16fde19ff2..ad55211c6a 100755 --- a/script/graphql/utils/process-schemas.js +++ b/script/graphql/utils/process-schemas.js @@ -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) diff --git a/script/graphql/utils/schema-helpers.js b/script/graphql/utils/schema-helpers.js index 80ed730b7d..21958e3601 100644 --- a/script/graphql/utils/schema-helpers.js +++ b/script/graphql/utils/schema-helpers.js @@ -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, diff --git a/server.js b/server.js index b6842357fe..16e77e7be8 100644 --- a/server.js +++ b/server.js @@ -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') { diff --git a/tests/browser/browser.js b/tests/browser/browser.js index 88138c7ed5..6c1b12e809 100644 --- a/tests/browser/browser.js +++ b/tests/browser/browser.js @@ -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) diff --git a/tests/content/graphql.js b/tests/content/graphql.js index 67d2735f6b..3f0a3216a8 100644 --- a/tests/content/graphql.js +++ b/tests/content/graphql.js @@ -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) diff --git a/tests/fixtures/feature-versions-frontmatter.md b/tests/fixtures/feature-versions-frontmatter.md new file mode 100644 index 0000000000..3b34a276c3 --- /dev/null +++ b/tests/fixtures/feature-versions-frontmatter.md @@ -0,0 +1,7 @@ +--- +title: Some article only versioned for FPT +versions: + fpt: '*' + ghes: '>2.21' + feature: 'placeholder' +--- diff --git a/tests/graphql/build-changelog-test.js b/tests/graphql/build-changelog-test.js index e27f2f86a8..7c04d2edf5 100644 --- a/tests/graphql/build-changelog-test.js +++ b/tests/graphql/build-changelog-test.js @@ -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(() => { diff --git a/tests/helpers/schemas/feature-versions-schema.js b/tests/helpers/schemas/feature-versions-schema.js new file mode 100644 index 0000000000..15b0e662e8 --- /dev/null +++ b/tests/helpers/schemas/feature-versions-schema.js @@ -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 diff --git a/tests/helpers/supertest.js b/tests/helpers/supertest.js index 60ab1b3b1d..978a61a79e 100644 --- a/tests/helpers/supertest.js +++ b/tests/helpers/supertest.js @@ -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 = {} diff --git a/tests/linting/lint-files.js b/tests/linting/lint-files.js index 40931f25b0..137f1813e0 100644 --- a/tests/linting/lint-files.js +++ b/tests/linting/lint-files.js @@ -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)) diff --git a/tests/rendering/events.js b/tests/rendering/events.js index 2ec5d7eca3..6ecaf402d7 100644 --- a/tests/rendering/events.js +++ b/tests/rendering/events.js @@ -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 diff --git a/tests/rendering/header.js b/tests/rendering/header.js index 4c62f7592d..6e4803fb75 100644 --- a/tests/rendering/header.js +++ b/tests/rendering/header.js @@ -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) }) diff --git a/tests/rendering/server.js b/tests/rendering/server.js index b0788fdd5f..fcd003a773 100644 --- a/tests/rendering/server.js +++ b/tests/rendering/server.js @@ -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) diff --git a/tests/rendering/sidebar.js b/tests/rendering/sidebar.js index 08d72cb4e0..f4af309cbc 100644 --- a/tests/rendering/sidebar.js +++ b/tests/rendering/sidebar.js @@ -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 () => { diff --git a/tests/routing/deprecated-enterprise-versions.js b/tests/routing/deprecated-enterprise-versions.js index e540218894..123527631b 100644 --- a/tests/routing/deprecated-enterprise-versions.js +++ b/tests/routing/deprecated-enterprise-versions.js @@ -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') diff --git a/tests/routing/developer-site-redirects.js b/tests/routing/developer-site-redirects.js index 53a776d34b..89e75099c4 100644 --- a/tests/routing/developer-site-redirects.js +++ b/tests/routing/developer-site-redirects.js @@ -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 diff --git a/tests/routing/redirects.js b/tests/routing/redirects.js index 7e0caafea5..70ee627186 100644 --- a/tests/routing/redirects.js +++ b/tests/routing/redirects.js @@ -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') }) diff --git a/tests/routing/top-developer-site-path-redirects.js b/tests/routing/top-developer-site-path-redirects.js index 5df215832c..ee033f8cb6 100644 --- a/tests/routing/top-developer-site-path-redirects.js +++ b/tests/routing/top-developer-site-path-redirects.js @@ -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) diff --git a/tests/unit/feature-flags.js b/tests/unit/feature-flags.js index 4b41ada884..18be5f8207 100644 --- a/tests/unit/feature-flags.js +++ b/tests/unit/feature-flags.js @@ -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 => { diff --git a/tests/unit/liquid.js b/tests/unit/liquid.js index 2ef7148489..c25a1b659d 100644 --- a/tests/unit/liquid.js +++ b/tests/unit/liquid.js @@ -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) + }) + }) }) diff --git a/tests/unit/page.js b/tests/unit/page.js index a40fcfd33b..8f38b2de32 100644 --- a/tests/unit/page.js +++ b/tests/unit/page.js @@ -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', () => { diff --git a/tests/unit/products.js b/tests/unit/products.js index a84f3c2ebc..f5b388ccc1 100644 --- a/tests/unit/products.js +++ b/tests/unit/products.js @@ -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') diff --git a/tests/unit/versions.js b/tests/unit/versions.js index 45b136115f..95e53ce35a 100644 --- a/tests/unit/versions.js +++ b/tests/unit/versions.js @@ -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)