diff --git a/assets/images/help/desktop/cherry-picking.png b/assets/images/help/desktop/cherry-picking.png new file mode 100644 index 0000000000..53831e8afe Binary files /dev/null and b/assets/images/help/desktop/cherry-picking.png differ diff --git a/content/actions/reference/environment-variables.md b/content/actions/reference/environment-variables.md index dd45eafec2..3acefddf75 100644 --- a/content/actions/reference/environment-variables.md +++ b/content/actions/reference/environment-variables.md @@ -22,6 +22,7 @@ versions: To set custom environment variables, you need to specify the variables in the workflow file. You can define environment variables for a step, job, or entire workflow using the [`jobs..steps[*].env`](/github/automating-your-workflow-with-github-actions/workflow-syntax-for-github-actions#jobsjob_idstepsenv), [`jobs..env`](/github/automating-your-workflow-with-github-actions/workflow-syntax-for-github-actions#jobsjob_idenv), and [`env`](/github/automating-your-workflow-with-github-actions/workflow-syntax-for-github-actions#env) keywords. For more information, see "[Workflow syntax for {% data variables.product.prodname_dotcom %}](/articles/workflow-syntax-for-github-actions/#jobsjob_idstepsenv)." +{% raw %} ```yaml jobs: weekday_job: @@ -30,13 +31,14 @@ jobs: DAY_OF_WEEK: Mon steps: - name: "Hello world when it's Monday" - if: env.DAY_OF_WEEK == 'Mon' + if: ${{ env.DAY_OF_WEEK == 'Mon' }} run: echo "Hello $FIRST_NAME $middle_name $Last_Name, today is Monday!" env: FIRST_NAME: Mona middle_name: The Last_Name: Octocat ``` +{% endraw %} To use the value of an environment variable in a workflow file, you should use the [`env` context](/actions/reference/context-and-expression-syntax-for-github-actions#env-context). If you want to use the value of an environment variable inside a runner, you can use the runner operating system's normal method for reading environment variables. diff --git a/content/actions/reference/environments.md b/content/actions/reference/environments.md index 7ce48e112e..d0e0ca0360 100644 --- a/content/actions/reference/environments.md +++ b/content/actions/reference/environments.md @@ -16,7 +16,7 @@ versions: You can configure environments with protection rules and secrets. When a workflow job references an environment, the job won't start until all of the environment's protection rules pass. A job also cannot access secrets that are defined in an environment until all the environment protection rules pass. {% if currentVersion == "free-pro-team@latest" %} -Environment protection rules and environment secrets are only available on public repositories. If you convert a repository from public to private, any configured protection rules or environment secrets will be ignored, and you will not be able to configure any environments. If you convert your repository back to public, you will have access to any previously configured protection rules and environment secrets. +Environment protection rules and environment secrets are only available on public repositories and private repositories on an enterprise plan. If you convert a repository from public to private on a non-enterprise plan, any configured protection rules or environment secrets will be ignored, and you will not be able to configure any environments. If you convert your repository back to public, you will have access to any previously configured protection rules and environment secrets. {% endif %} #### Environment protection rules diff --git a/content/desktop/contributing-and-collaborating-using-github-desktop/cherry-picking-a-commit.md b/content/desktop/contributing-and-collaborating-using-github-desktop/cherry-picking-a-commit.md new file mode 100644 index 0000000000..9b54e40276 --- /dev/null +++ b/content/desktop/contributing-and-collaborating-using-github-desktop/cherry-picking-a-commit.md @@ -0,0 +1,23 @@ +--- +title: Cherry-picking a commit +intro: You can pick a specific commit on one branch and copy the commit to another branch. +versions: + free-pro-team: '*' +--- + +### About Git cherry-pick + +You can cherry-pick a commit on one branch to create a copy of the commit with the same changes on another branch. If you commit changes to the wrong branch or want to make the same changes to another branch, you can cherry-pick the commit to apply the changes to another branch. You can also use cherry-picking to apply specific changes before you are ready to create or merge a pull request. For example, if you commit a bug fix to a feature branch, you can cherry-pick the commit with the bug fix to other branches of your project. + +You can also use cherry-picking when collaborating with a team. Some projects incorporate contributions by cherry-picking commits. For more information, see [Distributed Git - Maintaining a Project](https://git-scm.com/book/en/v2/Distributed-Git-Maintaining-a-Project#_rebase_cherry_pick) in the Git documentation. + +### Cherry-picking a commit + +{% data reusables.desktop.current-branch-menu %} +2. In the list of branches, click the branch that has the commit that you want to cherry-pick. +{% data reusables.desktop.history-tab %} +4. Drag the commit that you want to cherry-pick to the {% octicon "git-branch" aria-label="The branch icon" %} **Current Branch** menu and drop the commit on the branch that you want to copy the commit to. + ![Dragging a commit to another branch in the Current Branch menu](/assets/images/help/desktop/cherry-picking.png) + +### Further reading +- [git-cherry-pick](https://git-scm.com/docs/git-cherry-pick) in the Git documentation diff --git a/content/desktop/contributing-and-collaborating-using-github-desktop/index.md b/content/desktop/contributing-and-collaborating-using-github-desktop/index.md index 2e0f6b62fd..6156c18c83 100644 --- a/content/desktop/contributing-and-collaborating-using-github-desktop/index.md +++ b/content/desktop/contributing-and-collaborating-using-github-desktop/index.md @@ -22,6 +22,7 @@ versions: {% link_in_list /stashing-changes %} {% link_in_list /pushing-changes-to-github %} {% link_in_list /reverting-a-commit %} + {% link_in_list /cherry-picking-a-commit %} {% link_in_list /managing-tags %} {% link_in_list /viewing-the-branch-history %} {% topic_link_in_list /working-with-your-remote-repository-on-github-or-github-enterprise %} diff --git a/content/developers/webhooks-and-events/webhook-events-and-payloads.md b/content/developers/webhooks-and-events/webhook-events-and-payloads.md index 7ff7b44c42..e2a9c0974c 100644 --- a/content/developers/webhooks-and-events/webhook-events-and-payloads.md +++ b/content/developers/webhooks-and-events/webhook-events-and-payloads.md @@ -337,6 +337,60 @@ Key | Type | Description {{ webhookPayloadsForCurrentVersion.deployment_status }} +{% if currentVersion == "free-pro-team@latest" %} +### discussion + +{% data reusables.webhooks.discussions-webhooks-beta %} + +Activity related to a discussion. For more information, see the "[Using the GraphQL API for discussions](/graphql/guides/using-the-graphql-api-for-discussions)." +#### Availability + +- Repository webhooks +- Organization webhooks +- {% data variables.product.prodname_github_app %}s with the `discussions` permission + +#### Webhook payload object + +Key | Type | Description +----|------|------------- +`action` |`string` | The action performed. Can be `created`, `edited`, `deleted`, `pinned`, `unpinned`, `locked`, `unlocked`, `transferred`, `category_changed`, `answered`, or `unanswered`. +{% data reusables.webhooks.discussion_desc %} +{% data reusables.webhooks.repo_desc_graphql %} +{% data reusables.webhooks.org_desc_graphql %} +{% data reusables.webhooks.sender_desc %} + +#### Webhook payload example + +{{ webhookPayloadsForCurrentVersion.discussion.created }} + +### discussion_comment + +{% data reusables.webhooks.discussions-webhooks-beta %} + +Activity related to a comment in a discussion. For more information, see "[Using the GraphQL API for discussions](/graphql/guides/using-the-graphql-api-for-discussions)." + +#### Availability + +- Repository webhooks +- Organization webhooks +- {% data variables.product.prodname_github_app %}s with the `discussions` permission + +#### Webhook payload object + +Key | Type | Description +----|------|------------- +`action` |`string` | The action performed. Can be `created`, `edited`, or `deleted`. +`comment` | `object` | The [`discussion comment`](/graphql/guides/using-the-graphql-api-for-discussions#discussioncomment) resource. +{% data reusables.webhooks.discussion_desc %} +{% data reusables.webhooks.repo_desc_graphql %} +{% data reusables.webhooks.org_desc_graphql %} +{% data reusables.webhooks.sender_desc %} + +#### Webhook payload example + +{{ webhookPayloadsForCurrentVersion.discussion_comment.created }} +{% endif %} + {% if enterpriseServerVersions contains currentVersion or currentVersion == "github-ae@latest" %} ### enterprise diff --git a/content/github/working-with-github-pages/testing-your-github-pages-site-locally-with-jekyll.md b/content/github/working-with-github-pages/testing-your-github-pages-site-locally-with-jekyll.md index d24b5f19f7..32331bae67 100644 --- a/content/github/working-with-github-pages/testing-your-github-pages-site-locally-with-jekyll.md +++ b/content/github/working-with-github-pages/testing-your-github-pages-site-locally-with-jekyll.md @@ -30,6 +30,7 @@ Before you can use Jekyll to test a site, you must: {% data reusables.command_line.open_the_multi_os_terminal %} {% data reusables.pages.navigate-publishing-source %} +3. Run `bundle install`. 3. Run your Jekyll site locally. ```shell $ bundle exec jekyll serve diff --git a/content/github/working-with-github-pages/troubleshooting-custom-domains-and-github-pages.md b/content/github/working-with-github-pages/troubleshooting-custom-domains-and-github-pages.md index 1c6c9279c0..1ac902330c 100644 --- a/content/github/working-with-github-pages/troubleshooting-custom-domains-and-github-pages.md +++ b/content/github/working-with-github-pages/troubleshooting-custom-domains-and-github-pages.md @@ -23,8 +23,8 @@ Then, make sure the _CNAME_ file is formatted correctly. - The _CNAME_ filename must be all uppercase. - The _CNAME_ file can contain only one domain. To point multiple domains to your site, you must set up a redirect through your DNS provider. -- The _CNAME_ entry must be the bare domain. For example, `www.example.com`,`blog.example.com`, or `example.com`. -- The _CNAME_ entry can only be used once on {% data variables.product.product_name %}. For example, if another repository's _CNAME_ file contains `example.com`, you cannot use `example.com` in the _CNAME_ file for your repository. +- The _CNAME_ file must contain the domain name only. For example, `www.example.com`, `blog.example.com`, or `example.com`. +- The domain name must be unique across all {% data variables.product.prodname_pages %} sites. For example, if another repository's _CNAME_ file contains `example.com`, you cannot use `example.com` in the _CNAME_ file for your repository. ### DNS misconfiguration diff --git a/data/glossaries/external.yml b/data/glossaries/external.yml index 2b3295b411..6040ee6388 100644 --- a/data/glossaries/external.yml +++ b/data/glossaries/external.yml @@ -91,7 +91,7 @@ working tree is pointing to a new branch. - term: cherry-picking description: >- - To choose a subset of changes from a series of changes (typically commits) and record them as a new series of changes on top of a different codebase. In Git, this is performed by the `git cherry-pick` command to extract the change introduced by an existing commit on another branch and to record it based on the tip of the current branch as a new commit. + To choose a subset of changes from a series of changes (typically commits) and record them as a new series of changes on top of a different codebase. In Git, this is performed by the `git cherry-pick` command to extract the change introduced by an existing commit on another branch and to record it based on the tip of the current branch as a new commit. For more information, see [git-cherry-pick](https://git-scm.com/docs/git-cherry-pick) in the Git documentation. - term: child team description: >- Within nested teams, the subteam that inherits the parent team's access diff --git a/data/release-notes/2-21/17.yml b/data/release-notes/2-21/17.yml index 83825f2056..4f4ba8f132 100644 --- a/data/release-notes/2-21/17.yml +++ b/data/release-notes/2-21/17.yml @@ -1,4 +1,5 @@ date: '2021-03-23' +intro: Downloads have been disabled due to a major bug affecting multiple customers. A fix will be available in the next patch. sections: security_fixes: - '**HIGH:** A remote code execution vulnerability was identified in GitHub Enterprise Server that could be exploited when building a GitHub Pages site. User-controlled configuration options used by GitHub Pages were not sufficiently restricted and made it possible to override environment variables leading to code execution on the GitHub Enterprise Server instance. To exploit this vulnerability, an attacker would need permission to create and build a GitHub Pages site on the GitHub Enterprise Server instance. This vulnerability affected all versions of GitHub Enterprise Server prior to 3.0.3 and was fixed in 3.0.3, 2.22.9, and 2.21.17. This vulnerability was reported via the GitHub Bug Bounty program and has been assigned CVE-2021-22864.' diff --git a/data/release-notes/2-22/9.yml b/data/release-notes/2-22/9.yml index c64c545261..e567dad9ef 100644 --- a/data/release-notes/2-22/9.yml +++ b/data/release-notes/2-22/9.yml @@ -1,4 +1,5 @@ date: '2021-03-23' +intro: Downloads have been disabled due to a major bug affecting multiple customers. A fix will be available in the next patch. sections: security_fixes: - '**HIGH:** A remote code execution vulnerability was identified in GitHub Enterprise Server that could be exploited when building a GitHub Pages site. User-controlled configuration options used by GitHub Pages were not sufficiently restricted and made it possible to override environment variables leading to code execution on the GitHub Enterprise Server instance. To exploit this vulnerability, an attacker would need permission to create and build a GitHub Pages site on the GitHub Enterprise Server instance. This vulnerability affected all versions of GitHub Enterprise Server prior to 3.0.3 and was fixed in 3.0.3, 2.22.9, and 2.21.17. This vulnerability was reported via the GitHub Bug Bounty program and has been assigned CVE-2021-22864.' diff --git a/data/release-notes/3-0/0.yml b/data/release-notes/3-0/0.yml index e6fcc56191..dbab40a44b 100644 --- a/data/release-notes/3-0/0.yml +++ b/data/release-notes/3-0/0.yml @@ -131,6 +131,7 @@ sections: - Dependency graph fails to parse `setup.py` Python manifest files, resulting in HTTP 500 errors in logs. This, combined with the duplicated logging issue, results in increased root volume utilization. - A race condition can cause dependency graph database migrations to appear to fail. - Instances with a custom timezone that were upgraded from an earlier release of GitHub Enterprise Server may have incorrect timestamps in the web UI. + - Old builds of Pages are not cleaned up, which could fill up the user disk (`/data/user/`). deprecations: - heading: Deprecation of GitHub Enterprise Server 2.19 diff --git a/data/release-notes/3-0/1.yml b/data/release-notes/3-0/1.yml index 596affb9a0..c78862e536 100644 --- a/data/release-notes/3-0/1.yml +++ b/data/release-notes/3-0/1.yml @@ -43,6 +43,7 @@ sections: - 'reStructuredText (RST) rendering in the web UI may fail and instead display raw RST markup text.' - 'Dependency graph fails to parse `yarn.lock` Javascript manifest files, resulting in HTTP 500 errors in logs.' - 'Instances with a custom timezone that were upgraded from an earlier release of GitHub Enterprise Server may have incorrect timestamps in the web UI.' + - 'Old builds of Pages are not cleaned up, which could fill up the user disk (`/data/user/`).' - | Users may experience assets such as avatars not loading, or a failure to push/pull code. This may be caused by a PID mismatch in the `haproxy-cluster-proxy` service. To determine if you have an affected instance: diff --git a/data/release-notes/3-0/2.yml b/data/release-notes/3-0/2.yml index a4a2bf0e16..266143486d 100644 --- a/data/release-notes/3-0/2.yml +++ b/data/release-notes/3-0/2.yml @@ -29,6 +29,7 @@ sections: - When maintenance mode is enabled, some services continue to be listed as "active processes". The services identified are expected to run during maintenance mode. If you experience this issue and are unsure, contact [GitHub Enterprise Support](https://enterprise.githubsupport.com/hc/en-us) or [GitHub Premium Support](https://premium.githubsupport.com/). - Jupyter Notebook rendering in the web UI may fail if the notebook includes non-ASCII UTF-8 characters. - reStructuredText (RST) rendering in the web UI may fail and instead display raw RST markup text. + - Old builds of Pages are not cleaned up, which could fill up the user disk (`/data/user/`). - | Users may experience assets such as avatars not loading, or a failure to push/pull code. This may be caused by a PID mismatch in the `haproxy-cluster-proxy` service. To determine if you have an affected instance: diff --git a/data/release-notes/3-0/3.yml b/data/release-notes/3-0/3.yml index 2828755e4f..09b90b287c 100644 --- a/data/release-notes/3-0/3.yml +++ b/data/release-notes/3-0/3.yml @@ -1,5 +1,5 @@ date: '2021-03-23' -intro: The minimum infrastructure requirements have increased for {% data variables.product.prodname_ghe_server %} 3.0+. For more information, see "[About minimum requirements for GitHub Enterprise Server 3.0 and later](/admin/enterprise-management/upgrading-github-enterprise-server#about-minimum-requirements-for-github-enterprise-server-30-and-later)." +intro: Downloads have been disabled due to a major bug affecting multiple customers. A fix will be available in the next patch. sections: security_fixes: - '**HIGH:** A remote code execution vulnerability was identified in GitHub Enterprise Server that could be exploited when building a GitHub Pages site. User-controlled configuration options used by GitHub Pages were not sufficiently restricted and made it possible to override environment variables leading to code execution on the GitHub Enterprise Server instance. To exploit this vulnerability, an attacker would need permission to create and build a GitHub Pages site on the GitHub Enterprise Server instance. This vulnerability affected all versions of GitHub Enterprise Server prior to 3.0.3 and was fixed in 3.0.3, 2.22.9, and 2.21.17. This vulnerability was reported via the GitHub Bug Bounty program and has been assigned CVE-2021-22864.' @@ -28,6 +28,7 @@ sections: - When maintenance mode is enabled, some services continue to be listed as "active processes". The services identified are expected to run during maintenance mode. If you experience this issue and are unsure, contact [GitHub Enterprise Support](https://enterprise.githubsupport.com/hc/en-us) or [GitHub Premium Support](https://premium.githubsupport.com/). - Jupyter Notebook rendering in the web UI may fail if the notebook includes non-ASCII UTF-8 characters. - reStructuredText (RST) rendering in the web UI may fail and instead display raw RST markup text. + - Old builds of Pages are not cleaned up, which could fill up the user disk (`/data/user/`). - | Log rotation may fail to signal services to transition to new log files, leading to older log files continuing to be used, and eventual root disk space exhaustion. To remedy and/or prevent this issue, run the following commands in the [administrative shell](https://docs.github.com/en/enterprise-server/admin/configuration/accessing-the-administrative-shell-ssh) (SSH), or contact [GitHub Enterprise Support](https://enterprise.githubsupport.com/hc/en-us) or [GitHub Premium Support](https://premium.githubsupport.com/) for assistance: diff --git a/data/reusables/package_registry/authenticate-to-container-registry-steps.md b/data/reusables/package_registry/authenticate-to-container-registry-steps.md index bc748abafc..d5e173f71e 100644 --- a/data/reusables/package_registry/authenticate-to-container-registry-steps.md +++ b/data/reusables/package_registry/authenticate-to-container-registry-steps.md @@ -1,7 +1,7 @@ 1. Create a new personal access token (PAT) with the appropriate scopes for the tasks you want to accomplish. If your organization requires SSO, you must enable SSO for your new token. {% warning %} - **Note:** If you select the `write:packages` scope, deselect the `repo` scope when creating the PAT. Adding a PAT with the `repo` scope as a secret in your repository allows the credential to be accessible to all collaborators in the repository. This gives unnecessary additional access when a PAT with the `repo` scope is used within an action. For more information on security best practices for actions, see "[Security hardening for GitHub Actions](/actions/getting-started-with-github-actions/security-hardening-for-github-actions#considering-cross-repository-access)." + **Note:** By default, when you select the `write:packages` scope for your personal access token (PAT) in the user interface, the `repo` scope will also be selected. The `repo` scope offers unnecessary and broad access, which we recommend you avoid using for GitHub Actions workflows in particular. For more information, see "[Security hardening for GitHub Actions](/actions/getting-started-with-github-actions/security-hardening-for-github-actions#considering-cross-repository-access)." As a workaround, you can select just the `write:packages` scope for your PAT in the user interface with this url: `https://github.com/settings/tokens/new?scopes=write:packages`. {% endwarning %} diff --git a/data/reusables/package_registry/authenticate_with_pat_for_container_registry.md b/data/reusables/package_registry/authenticate_with_pat_for_container_registry.md index fb85de55b5..666a293400 100644 --- a/data/reusables/package_registry/authenticate_with_pat_for_container_registry.md +++ b/data/reusables/package_registry/authenticate_with_pat_for_container_registry.md @@ -1,5 +1,7 @@ {% if currentVersion == "free-pro-team@latest" %} +PATs can grant broad access to your account. You should select only the necessary `read:packages`, `write:packages`, or `delete:packages` scope when creating a PAT to authenticate to the {% data variables.product.prodname_container_registry %}. + To authenticate to {% data variables.product.prodname_github_container_registry %} within a {% data variables.product.prodname_actions %} workflow, use the `GITHUB_TOKEN` for the best security and experience. For guidance on updating your workflows that authenticate to `ghcr.io` with a personal access token, see "[Upgrading a workflow that accesses `ghcr.io`](/packages/guides/using-github-packages-with-github-actions#upgrading-a-workflow-that-accesses-ghcrio)." diff --git a/data/reusables/webhooks/discussion_desc.md b/data/reusables/webhooks/discussion_desc.md new file mode 100644 index 0000000000..1b221660e0 --- /dev/null +++ b/data/reusables/webhooks/discussion_desc.md @@ -0,0 +1 @@ +`discussion` | `object` | The [`discussion`](/graphql/guides/using-the-graphql-api-for-discussions#discussion) resource. \ No newline at end of file diff --git a/data/reusables/webhooks/discussions-webhooks-beta.md b/data/reusables/webhooks/discussions-webhooks-beta.md new file mode 100644 index 0000000000..2033ca20ea --- /dev/null +++ b/data/reusables/webhooks/discussions-webhooks-beta.md @@ -0,0 +1,5 @@ +{% note %} + +**Note:** Webhook events for {% data variables.product.prodname_discussions %} are currently in beta and subject to change. + +{% endnote %} \ No newline at end of file diff --git a/data/reusables/webhooks/org_desc_graphql.md b/data/reusables/webhooks/org_desc_graphql.md new file mode 100644 index 0000000000..4bd5d803b1 --- /dev/null +++ b/data/reusables/webhooks/org_desc_graphql.md @@ -0,0 +1 @@ +`organization` | `object` | Webhook payloads contain the [`organization`](/graphql/reference/objects#organization) object when the webhook is configured for an organization or the event occurs from activity in a repository owned by an organization. diff --git a/data/reusables/webhooks/repo_desc_graphql.md b/data/reusables/webhooks/repo_desc_graphql.md new file mode 100644 index 0000000000..7cfc041ade --- /dev/null +++ b/data/reusables/webhooks/repo_desc_graphql.md @@ -0,0 +1 @@ +`repository` | `object` | The [`repository`](/graphql/reference/objects#repository) where the event occurred. \ No newline at end of file diff --git a/lib/webhooks/static/dotcom/discussion.created.payload.json b/lib/webhooks/static/dotcom/discussion.created.payload.json new file mode 100644 index 0000000000..9739b04f3c --- /dev/null +++ b/lib/webhooks/static/dotcom/discussion.created.payload.json @@ -0,0 +1,181 @@ +{ + "action": "created", + "discussion": { + "repository_url": "https://api.github.com/repos/octo-org/octo-repo", + "category": { + "id": 32784361, + "repository_id": 17273051, + "emoji": ":speech_balloon:", + "name": "General", + "description": "Chat about anything and everything here", + "created_at": "2021-03-24T12:41:54.000-05:00", + "updated_at": "2021-03-24T12:41:54.000-05:00", + "slug": "general", + "is_answerable": false + }, + "answer_html_url": null, + "answer_chosen_at": null, + "answer_chosen_by": null, + "html_url": "https://github.com/octo-org/octo-repo/discussions/90", + "id": 3297442, + "node_id": "MDEwOkRpc2N1c3Npb24zMjk3NDQy", + "number": 90, + "title": "Welcome to discussions!", + "user": { + "login": "Codertocat", + "id": 21031067, + "node_id": "MDQ6VXNlcjIxMDMxMDY3", + "avatar_url": "https://avatars1.githubusercontent.com/u/21031067?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/Codertocat", + "html_url": "https://github.com/Codertocat", + "followers_url": "https://api.github.com/users/Codertocat/followers", + "following_url": "https://api.github.com/users/Codertocat/following{/other_user}", + "gists_url": "https://api.github.com/users/Codertocat/gists{/gist_id}", + "starred_url": "https://api.github.com/users/Codertocat/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/Codertocat/subscriptions", + "organizations_url": "https://api.github.com/users/Codertocat/orgs", + "repos_url": "https://api.github.com/users/Codertocat/repos", + "events_url": "https://api.github.com/users/Codertocat/events{/privacy}", + "received_events_url": "https://api.github.com/users/Codertocat/received_events", + "type": "User", + "site_admin": false + }, + "state": "open", + "locked": false, + "comments": 0, + "created_at": "2021-03-29T14:16:08Z", + "updated_at": "2021-03-29T14:16:08Z", + "author_association": "COLLABORATOR", + "active_lock_reason": null, + "body": "We're glad to have you here!" + }, + "repository": { + "id": 17273051, + "node_id": "MDEwOlJlcG9zaXRvcnkxNzI3MzA1MQ==", + "name": "octo-repo", + "full_name": "octo-org/octo-repo", + "private": true, + "owner": { + "login": "octo-org", + "id": 6811672, + "node_id": "MDEyOk9yZ2FuaXphdGlvbjY4MTE2NzI=", + "avatar_url": "https://avatars.githubusercontent.com/u/6811672?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/octo-org", + "html_url": "https://github.com/octo-org", + "followers_url": "https://api.github.com/users/octo-org/followers", + "following_url": "https://api.github.com/users/octo-org/following{/other_user}", + "gists_url": "https://api.github.com/users/octo-org/gists{/gist_id}", + "starred_url": "https://api.github.com/users/octo-org/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/octo-org/subscriptions", + "organizations_url": "https://api.github.com/users/octo-org/orgs", + "repos_url": "https://api.github.com/users/octo-org/repos", + "events_url": "https://api.github.com/users/octo-org/events{/privacy}", + "received_events_url": "https://api.github.com/users/octo-org/received_events", + "type": "Organization", + "site_admin": false + }, + "html_url": "https://github.com/octo-org/octo-repo", + "description": "My first repo on GitHub!", + "fork": false, + "url": "https://api.github.com/repos/octo-org/octo-repo", + "forks_url": "https://api.github.com/repos/octo-org/octo-repo/forks", + "keys_url": "https://api.github.com/repos/octo-org/octo-repo/keys{/key_id}", + "collaborators_url": "https://api.github.com/repos/octo-org/octo-repo/collaborators{/collaborator}", + "teams_url": "https://api.github.com/repos/octo-org/octo-repo/teams", + "hooks_url": "https://api.github.com/repos/octo-org/octo-repo/hooks", + "issue_events_url": "https://api.github.com/repos/octo-org/octo-repo/issues/events{/number}", + "events_url": "https://api.github.com/repos/octo-org/octo-repo/events", + "assignees_url": "https://api.github.com/repos/octo-org/octo-repo/assignees{/user}", + "branches_url": "https://api.github.com/repos/octo-org/octo-repo/branches{/branch}", + "tags_url": "https://api.github.com/repos/octo-org/octo-repo/tags", + "blobs_url": "https://api.github.com/repos/octo-org/octo-repo/git/blobs{/sha}", + "git_tags_url": "https://api.github.com/repos/octo-org/octo-repo/git/tags{/sha}", + "git_refs_url": "https://api.github.com/repos/octo-org/octo-repo/git/refs{/sha}", + "trees_url": "https://api.github.com/repos/octo-org/octo-repo/git/trees{/sha}", + "statuses_url": "https://api.github.com/repos/octo-org/octo-repo/statuses/{sha}", + "languages_url": "https://api.github.com/repos/octo-org/octo-repo/languages", + "stargazers_url": "https://api.github.com/repos/octo-org/octo-repo/stargazers", + "contributors_url": "https://api.github.com/repos/octo-org/octo-repo/contributors", + "subscribers_url": "https://api.github.com/repos/octo-org/octo-repo/subscribers", + "subscription_url": "https://api.github.com/repos/octo-org/octo-repo/subscription", + "commits_url": "https://api.github.com/repos/octo-org/octo-repo/commits{/sha}", + "git_commits_url": "https://api.github.com/repos/octo-org/octo-repo/git/commits{/sha}", + "comments_url": "https://api.github.com/repos/octo-org/octo-repo/comments{/number}", + "issue_comment_url": "https://api.github.com/repos/octo-org/octo-repo/issues/comments{/number}", + "contents_url": "https://api.github.com/repos/octo-org/octo-repo/contents/{+path}", + "compare_url": "https://api.github.com/repos/octo-org/octo-repo/compare/{base}...{head}", + "merges_url": "https://api.github.com/repos/octo-org/octo-repo/merges", + "archive_url": "https://api.github.com/repos/octo-org/octo-repo/{archive_format}{/ref}", + "downloads_url": "https://api.github.com/repos/octo-org/octo-repo/downloads", + "issues_url": "https://api.github.com/repos/octo-org/octo-repo/issues{/number}", + "pulls_url": "https://api.github.com/repos/octo-org/octo-repo/pulls{/number}", + "milestones_url": "https://api.github.com/repos/octo-org/octo-repo/milestones{/number}", + "notifications_url": "https://api.github.com/repos/octo-org/octo-repo/notifications{?since,all,participating}", + "labels_url": "https://api.github.com/repos/octo-org/octo-repo/labels{/name}", + "releases_url": "https://api.github.com/repos/octo-org/octo-repo/releases{/id}", + "deployments_url": "https://api.github.com/repos/octo-org/octo-repo/deployments", + "created_at": "2014-02-28T02:42:51Z", + "updated_at": "2021-03-11T14:54:13Z", + "pushed_at": "2021-03-11T14:54:10Z", + "git_url": "git://github.com/octo-org/octo-repo.git", + "ssh_url": "org-6811672@github.com:octo-org/octo-repo.git", + "clone_url": "https://github.com/octo-org/octo-repo.git", + "svn_url": "https://github.com/octo-org/octo-repo", + "homepage": "", + "size": 300, + "stargazers_count": 0, + "watchers_count": 0, + "language": "JavaScript", + "has_issues": true, + "has_projects": false, + "has_downloads": true, + "has_wiki": false, + "has_pages": true, + "forks_count": 0, + "mirror_url": null, + "archived": false, + "disabled": false, + "open_issues_count": 39, + "license": null, + "forks": 0, + "open_issues": 39, + "watchers": 0, + "default_branch": "main" + }, + "organization": { + "login": "octo-org", + "id": 6811672, + "node_id": "MDEyOk9yZ2FuaXphdGlvbjY4MTE2NzI=", + "url": "https://api.github.com/orgs/octo-org", + "repos_url": "https://api.github.com/orgs/octo-org/repos", + "events_url": "https://api.github.com/orgs/octo-org/events", + "hooks_url": "https://api.github.com/orgs/octo-org/hooks", + "issues_url": "https://api.github.com/orgs/octo-org/issues", + "members_url": "https://api.github.com/orgs/octo-org/members{/member}", + "public_members_url": "https://api.github.com/orgs/octo-org/public_members{/member}", + "avatar_url": "https://avatars.githubusercontent.com/u/6811672?v=4", + "description": "Working better together!" + }, + "sender": { + "login": "Codertocat", + "id": 21031067, + "node_id": "MDQ6VXNlcjIxMDMxMDY3", + "avatar_url": "https://avatars1.githubusercontent.com/u/21031067?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/Codertocat", + "html_url": "https://github.com/Codertocat", + "followers_url": "https://api.github.com/users/Codertocat/followers", + "following_url": "https://api.github.com/users/Codertocat/following{/other_user}", + "gists_url": "https://api.github.com/users/Codertocat/gists{/gist_id}", + "starred_url": "https://api.github.com/users/Codertocat/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/Codertocat/subscriptions", + "organizations_url": "https://api.github.com/users/Codertocat/orgs", + "repos_url": "https://api.github.com/users/Codertocat/repos", + "events_url": "https://api.github.com/users/Codertocat/events{/privacy}", + "received_events_url": "https://api.github.com/users/Codertocat/received_events", + "type": "User", + "site_admin": false + } +} \ No newline at end of file diff --git a/lib/webhooks/static/dotcom/discussion_comment.created.payload.json b/lib/webhooks/static/dotcom/discussion_comment.created.payload.json new file mode 100644 index 0000000000..03f7b4f387 --- /dev/null +++ b/lib/webhooks/static/dotcom/discussion_comment.created.payload.json @@ -0,0 +1,214 @@ +{ + "action": "created", + "comment": { + "id": 544078, + "node_id": "MDE3OkRpc2N1c3Npb25Db21tZW50NTQ0MDc4", + "html_url": "https://github.com/octo-org/octo-repo/discussions/90#discussioncomment-544078", + "parent_id": null, + "child_comment_count": 0, + "repository_url": "octo-org/octo-repo", + "discussion_id": 3297442, + "author_association": "COLLABORATOR", + "user": { + "login": "Codertocat", + "id": 21031067, + "node_id": "MDQ6VXNlcjIxMDMxMDY3", + "avatar_url": "https://avatars1.githubusercontent.com/u/21031067?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/Codertocat", + "html_url": "https://github.com/Codertocat", + "followers_url": "https://api.github.com/users/Codertocat/followers", + "following_url": "https://api.github.com/users/Codertocat/following{/other_user}", + "gists_url": "https://api.github.com/users/Codertocat/gists{/gist_id}", + "starred_url": "https://api.github.com/users/Codertocat/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/Codertocat/subscriptions", + "organizations_url": "https://api.github.com/users/Codertocat/orgs", + "repos_url": "https://api.github.com/users/Codertocat/repos", + "events_url": "https://api.github.com/users/Codertocat/events{/privacy}", + "received_events_url": "https://api.github.com/users/Codertocat/received_events", + "type": "User", + "site_admin": false + }, + "created_at": "2021-03-29T14:16:31Z", + "updated_at": "2021-03-29T14:16:31Z", + "body": "I have so many questions to ask you!" + }, + "discussion": { + "repository_url": "https://api.github.com/repos/octo-org/octo-repo", + "category": { + "id": 32784361, + "repository_id": 17273051, + "emoji": ":speech_balloon:", + "name": "General", + "description": "Chat about anything and everything here", + "created_at": "2021-03-24T12:41:54.000-05:00", + "updated_at": "2021-03-24T12:41:54.000-05:00", + "slug": "general", + "is_answerable": false + }, + "answer_html_url": null, + "answer_chosen_at": null, + "answer_chosen_by": null, + "html_url": "https://github.com/octo-org/octo-repo/discussions/90", + "id": 3297442, + "node_id": "MDEwOkRpc2N1c3Npb24zMjk3NDQy", + "number": 90, + "title": "Welcome to discussions!", + "user": { + "login": "Codertocat", + "id": 14935376, + "node_id": "MDQ6VXNlcjE0OTM1Mzc2", + "avatar_url": "https://avatars.githubusercontent.com/u/14935376?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/Codertocat", + "html_url": "https://github.com/Codertocat", + "followers_url": "https://api.github.com/users/Codertocat/followers", + "following_url": "https://api.github.com/users/Codertocat/following{/other_user}", + "gists_url": "https://api.github.com/users/Codertocat/gists{/gist_id}", + "starred_url": "https://api.github.com/users/Codertocat/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/Codertocat/subscriptions", + "organizations_url": "https://api.github.com/users/Codertocat/orgs", + "repos_url": "https://api.github.com/users/Codertocat/repos", + "events_url": "https://api.github.com/users/Codertocat/events{/privacy}", + "received_events_url": "https://api.github.com/users/Codertocat/received_events", + "type": "User", + "site_admin": true + }, + "state": "open", + "locked": false, + "comments": 1, + "created_at": "2021-03-29T14:16:08Z", + "updated_at": "2021-03-29T14:16:31Z", + "author_association": "COLLABORATOR", + "active_lock_reason": null, + "body": "We're glad to have you here!" + }, + "repository": { + "id": 17273051, + "node_id": "MDEwOlJlcG9zaXRvcnkxNzI3MzA1MQ==", + "name": "octo-repo", + "full_name": "octo-org/octo-repo", + "private": true, + "owner": { + "login": "octo-org", + "id": 6811672, + "node_id": "MDEyOk9yZ2FuaXphdGlvbjY4MTE2NzI=", + "avatar_url": "https://avatars.githubusercontent.com/u/6811672?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/octo-org", + "html_url": "https://github.com/octo-org", + "followers_url": "https://api.github.com/users/octo-org/followers", + "following_url": "https://api.github.com/users/octo-org/following{/other_user}", + "gists_url": "https://api.github.com/users/octo-org/gists{/gist_id}", + "starred_url": "https://api.github.com/users/octo-org/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/octo-org/subscriptions", + "organizations_url": "https://api.github.com/users/octo-org/orgs", + "repos_url": "https://api.github.com/users/octo-org/repos", + "events_url": "https://api.github.com/users/octo-org/events{/privacy}", + "received_events_url": "https://api.github.com/users/octo-org/received_events", + "type": "Organization", + "site_admin": false + }, + "html_url": "https://github.com/octo-org/octo-repo", + "description": "My first repo on GitHub!", + "fork": false, + "url": "https://api.github.com/repos/octo-org/octo-repo", + "forks_url": "https://api.github.com/repos/octo-org/octo-repo/forks", + "keys_url": "https://api.github.com/repos/octo-org/octo-repo/keys{/key_id}", + "collaborators_url": "https://api.github.com/repos/octo-org/octo-repo/collaborators{/collaborator}", + "teams_url": "https://api.github.com/repos/octo-org/octo-repo/teams", + "hooks_url": "https://api.github.com/repos/octo-org/octo-repo/hooks", + "issue_events_url": "https://api.github.com/repos/octo-org/octo-repo/issues/events{/number}", + "events_url": "https://api.github.com/repos/octo-org/octo-repo/events", + "assignees_url": "https://api.github.com/repos/octo-org/octo-repo/assignees{/user}", + "branches_url": "https://api.github.com/repos/octo-org/octo-repo/branches{/branch}", + "tags_url": "https://api.github.com/repos/octo-org/octo-repo/tags", + "blobs_url": "https://api.github.com/repos/octo-org/octo-repo/git/blobs{/sha}", + "git_tags_url": "https://api.github.com/repos/octo-org/octo-repo/git/tags{/sha}", + "git_refs_url": "https://api.github.com/repos/octo-org/octo-repo/git/refs{/sha}", + "trees_url": "https://api.github.com/repos/octo-org/octo-repo/git/trees{/sha}", + "statuses_url": "https://api.github.com/repos/octo-org/octo-repo/statuses/{sha}", + "languages_url": "https://api.github.com/repos/octo-org/octo-repo/languages", + "stargazers_url": "https://api.github.com/repos/octo-org/octo-repo/stargazers", + "contributors_url": "https://api.github.com/repos/octo-org/octo-repo/contributors", + "subscribers_url": "https://api.github.com/repos/octo-org/octo-repo/subscribers", + "subscription_url": "https://api.github.com/repos/octo-org/octo-repo/subscription", + "commits_url": "https://api.github.com/repos/octo-org/octo-repo/commits{/sha}", + "git_commits_url": "https://api.github.com/repos/octo-org/octo-repo/git/commits{/sha}", + "comments_url": "https://api.github.com/repos/octo-org/octo-repo/comments{/number}", + "issue_comment_url": "https://api.github.com/repos/octo-org/octo-repo/issues/comments{/number}", + "contents_url": "https://api.github.com/repos/octo-org/octo-repo/contents/{+path}", + "compare_url": "https://api.github.com/repos/octo-org/octo-repo/compare/{base}...{head}", + "merges_url": "https://api.github.com/repos/octo-org/octo-repo/merges", + "archive_url": "https://api.github.com/repos/octo-org/octo-repo/{archive_format}{/ref}", + "downloads_url": "https://api.github.com/repos/octo-org/octo-repo/downloads", + "issues_url": "https://api.github.com/repos/octo-org/octo-repo/issues{/number}", + "pulls_url": "https://api.github.com/repos/octo-org/octo-repo/pulls{/number}", + "milestones_url": "https://api.github.com/repos/octo-org/octo-repo/milestones{/number}", + "notifications_url": "https://api.github.com/repos/octo-org/octo-repo/notifications{?since,all,participating}", + "labels_url": "https://api.github.com/repos/octo-org/octo-repo/labels{/name}", + "releases_url": "https://api.github.com/repos/octo-org/octo-repo/releases{/id}", + "deployments_url": "https://api.github.com/repos/octo-org/octo-repo/deployments", + "created_at": "2014-02-28T02:42:51Z", + "updated_at": "2021-03-11T14:54:13Z", + "pushed_at": "2021-03-11T14:54:10Z", + "git_url": "git://github.com/octo-org/octo-repo.git", + "ssh_url": "org-6811672@github.com:octo-org/octo-repo.git", + "clone_url": "https://github.com/octo-org/octo-repo.git", + "svn_url": "https://github.com/octo-org/octo-repo", + "homepage": "", + "size": 300, + "stargazers_count": 0, + "watchers_count": 0, + "language": "JavaScript", + "has_issues": true, + "has_projects": false, + "has_downloads": true, + "has_wiki": false, + "has_pages": true, + "forks_count": 0, + "mirror_url": null, + "archived": false, + "disabled": false, + "open_issues_count": 39, + "license": null, + "forks": 0, + "open_issues": 39, + "watchers": 0, + "default_branch": "main" + }, + "organization": { + "login": "octo-org", + "id": 6811672, + "node_id": "MDEyOk9yZ2FuaXphdGlvbjY4MTE2NzI=", + "url": "https://api.github.com/orgs/octo-org", + "repos_url": "https://api.github.com/orgs/octo-org/repos", + "events_url": "https://api.github.com/orgs/octo-org/events", + "hooks_url": "https://api.github.com/orgs/octo-org/hooks", + "issues_url": "https://api.github.com/orgs/octo-org/issues", + "members_url": "https://api.github.com/orgs/octo-org/members{/member}", + "public_members_url": "https://api.github.com/orgs/octo-org/public_members{/member}", + "avatar_url": "https://avatars.githubusercontent.com/u/6811672?v=4", + "description": "Working better together!" + }, + "sender": { + "login": "Codertocat", + "id": 21031067, + "node_id": "MDQ6VXNlcjIxMDMxMDY3", + "avatar_url": "https://avatars1.githubusercontent.com/u/21031067?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/Codertocat", + "html_url": "https://github.com/Codertocat", + "followers_url": "https://api.github.com/users/Codertocat/followers", + "following_url": "https://api.github.com/users/Codertocat/following{/other_user}", + "gists_url": "https://api.github.com/users/Codertocat/gists{/gist_id}", + "starred_url": "https://api.github.com/users/Codertocat/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/Codertocat/subscriptions", + "organizations_url": "https://api.github.com/users/Codertocat/orgs", + "repos_url": "https://api.github.com/users/Codertocat/repos", + "events_url": "https://api.github.com/users/Codertocat/events{/privacy}", + "received_events_url": "https://api.github.com/users/Codertocat/received_events", + "type": "User", + "site_admin": false + } +} diff --git a/package-lock.json b/package-lock.json index e891d6fabb..eae384d420 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7962,11 +7962,6 @@ "mimic-response": "^1.0.0" } }, - "cluster-key-slot": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.0.tgz", - "integrity": "sha512-2Nii8p3RwAPiFwsnZvukotvow2rIHM+yQ6ZcBXGHdniadkYGZYiGmkHJIbZPIV9nfv7m/U1IPMVVcAhoWFeklw==" - }, "co": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", @@ -13438,43 +13433,6 @@ "loose-envify": "^1.0.0" } }, - "ioredis": { - "version": "4.24.4", - "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-4.24.4.tgz", - "integrity": "sha512-v28xxBENyTmReC6lVTL7EkAPjVF8cuGtDEjGDi1Z2n7htsC2WdiDceZrCIPeuPiLa6kDFWHb1Y8O0upZROsMgA==", - "requires": { - "cluster-key-slot": "^1.1.0", - "debug": "^4.3.1", - "denque": "^1.1.0", - "lodash.defaults": "^4.2.0", - "lodash.flatten": "^4.4.0", - "p-map": "^2.1.0", - "redis-commands": "1.7.0", - "redis-errors": "^1.2.0", - "redis-parser": "^3.0.0", - "standard-as-callback": "^2.1.0" - }, - "dependencies": { - "debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "requires": { - "ms": "2.1.2" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - }, - "redis-commands": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/redis-commands/-/redis-commands-1.7.0.tgz", - "integrity": "sha512-nJWqw3bTFy21hX/CPKHth6sfhZbdiHP6bTawSgQBlKOVRG7EZkfHbbHwQJnrE4vsQf0CMNE+3gJ4Fmm16vdVlQ==" - } - } - }, "ip-regex": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-2.1.0.tgz", @@ -18188,7 +18146,8 @@ "lodash.defaults": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", - "integrity": "sha1-0JF4cW/+pN3p5ft7N/bwgCJ0WAw=" + "integrity": "sha1-0JF4cW/+pN3p5ft7N/bwgCJ0WAw=", + "dev": true }, "lodash.filter": { "version": "4.6.0", @@ -18199,7 +18158,8 @@ "lodash.flatten": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz", - "integrity": "sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8=" + "integrity": "sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8=", + "dev": true }, "lodash.foreach": { "version": "4.5.0", @@ -19681,7 +19641,8 @@ "p-map": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/p-map/-/p-map-2.1.0.tgz", - "integrity": "sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==" + "integrity": "sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==", + "dev": true }, "p-queue": { "version": "6.2.1", @@ -22475,11 +22436,6 @@ "integrity": "sha512-GrdeshiRmS1YLMYgzF16olf2jJ/IzxXY9lhKOskuVziubpTYcYqyOwYeJKzQkwy7uN0fYSsbsC4RQaXf9LCrYA==", "dev": true }, - "standard-as-callback": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/standard-as-callback/-/standard-as-callback-2.1.0.tgz", - "integrity": "sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A==" - }, "start-server-and-test": { "version": "1.12.0", "resolved": "https://registry.npmjs.org/start-server-and-test/-/start-server-and-test-1.12.0.tgz", diff --git a/package.json b/package.json index 57dab2f197..3fb34066e8 100644 --- a/package.json +++ b/package.json @@ -58,7 +58,6 @@ "hot-shots": "^8.2.0", "html-entities": "^1.2.1", "imurmurhash": "^0.1.4", - "ioredis": "^4.24.4", "is-url": "^1.2.4", "js-cookie": "^2.2.1", "js-yaml": "^3.14.0", diff --git a/script/purge-redis-pages.js b/script/purge-redis-pages.js old mode 100644 new mode 100755 index 13f9ad98c3..0f0d429ef9 --- a/script/purge-redis-pages.js +++ b/script/purge-redis-pages.js @@ -8,7 +8,10 @@ // // [end-readme] -const Redis = require('ioredis') +require('dotenv').config() + +const { promisify } = require('util') +const createClient = require('../lib/redis/create-client') const { REDIS_URL, HEROKU_RELEASE_VERSION, HEROKU_PRODUCTION_APP } = process.env const isHerokuProd = HEROKU_PRODUCTION_APP === 'true' @@ -42,68 +45,109 @@ console.log({ purgeRenderedPageCache() function purgeRenderedPageCache () { - const redisClient = new Redis(REDIS_URL, { - db: pageCacheDatabaseNumber, - - // Only add this configuration for TLS-enabled REDIS_URL values. - // Otherwise, it breaks for local Redis instances without TLS enabled. - ...REDIS_URL.startsWith('rediss://') && { - tls: { - // Required for production Heroku Redis - rejectUnauthorized: false - } - } + const redisClient = createClient({ + url: REDIS_URL, + db: pageCacheDatabaseNumber }) - let totalKeyCount = 0 + let iteration = 0 + let potentialKeyCount = 0 + let totalKeyCount = 0 - // Create a readable stream (object mode) for the SCAN cursor - const scanStream = redisClient.scanStream({ - match: keyScanningPattern, - count: scanSetSize - }) + // Promise wrappers + const scanAsync = promisify(redisClient.scan).bind(redisClient) + const quitAsync = promisify(redisClient.quit).bind(redisClient) - scanStream.on('end', function () { - console.log(`Done purging keys; affected total: ${totalKeyCount}`) - console.log(`Time elapsed: ${Date.now() - startTime} ms`) + // Run it! + return scan() - // This seems to be unexpectedly necessary - process.exit(0) - }) + // + // Define other subroutines + // - scanStream.on('error', function (error) { - console.error('An unexpected error occurred!\n' + error.stack) - console.error('\nAborting...') - process.exit(1) - }) + async function scan (cursor = '0') { + try { + // [0]: Update the cursor position for the next scan + // [1]: Get the SCAN result for this iteration + const [nextCursor, keys] = await scanAsync( + cursor, + 'MATCH', keyScanningPattern, + 'COUNT', scanSetSize.toString() + ) - scanStream.on('data', async function (keys) { - console.log(`[Iteration ${iteration++}] Received ${keys.length} keys...`) + console.log(`\n[Iteration ${iteration++}] Received ${keys.length} keys...`) - // NOTE: It is possible for a SCAN cursor iteration to return 0 keys when - // using a MATCH because it is applied after the elements are retrieved - if (keys.length === 0) return + if (dryRun) { + console.log(`DRY RUN! This iteration might have set TTL for up to ${keys.length} keys:\n - ${keys.join('\n - ')}`) + } - if (dryRun) { - console.log(`DRY RUN! This iteration might have set TTL for up to ${keys.length} keys:\n - ${keys.join('\n - ')}`) - return + // NOTE: It is possible for a SCAN cursor iteration to return 0 keys when + // using a MATCH because it is applied after the elements are retrieved + // + // Remember: more or less than COUNT or no keys may be returned + // See http://redis.io/commands/scan#the-count-option + // Also, SCAN may return the same key multiple times + // See http://redis.io/commands/scan#scan-guarantees + // Additionally, you should always have the code that uses the keys + // before the code checking the cursor. + if (keys.length > 0) { + if (dryRun) { + potentialKeyCount += keys.length + } else { + totalKeyCount += await updateTtls(keys) + } + } + + // From : + // 'An iteration starts when the cursor is set to 0, + // and terminates when the cursor returned by the server is 0.' + if (nextCursor === '0') { + const dryRunTrailer = dryRun ? ` (potentially up to ${potentialKeyCount})` : '' + console.log(`\nDone purging keys; affected total: ${totalKeyCount}${dryRunTrailer}`) + console.log(`Time elapsed: ${Date.now() - startTime} ms`) + + // Close the connection + await quitAsync() + return + } + + // Tail recursion + return scan(nextCursor) + } catch (error) { + console.error('An unexpected error occurred!\n' + error.stack) + console.error('\nAborting...') + process.exit(1) + } + } + + // Find existing TTLs to ensure we aren't extending the TTL if it's already set + async function getTtls (keys) { + const pttlPipeline = redisClient.batch() + keys.forEach(key => pttlPipeline.pttl(key)) + + const pttlPipelineExecAsync = promisify(pttlPipeline.exec).bind(pttlPipeline) + const pttlResults = await pttlPipelineExecAsync() + + if (pttlResults == null || pttlResults.length === 0) { + throw new Error('PTTL results were empty') } - // Pause the SCAN stream while we set a TTL on these keys - scanStream.pause() + return pttlResults + } - // Find existing TTLs to ensure we aren't extending the TTL if it's already set - // PTTL mykey // only operate on -1 result values or those greater than ONE_HOUR_FROM_NOW - const pttlPipeline = redisClient.pipeline() - keys.forEach(key => pttlPipeline.pttl(key)) - const pttlResults = await pttlPipeline.exec() + async function updateTtls (keys) { + const pttlResults = await getTtls(keys) - // Update pertinent keys to have TTLs set + // Find pertinent keys to have TTLs set let updatingKeyCount = 0 - const pexpireAtPipeline = redisClient.pipeline() + const pexpireAtPipeline = redisClient.batch() + keys.forEach((key, i) => { - const [error, pttl] = pttlResults[i] - const needsShortenedTtl = error == null && (pttl === -1 || pttl > expirationDuration) + // Only operate on -1 result values or those greater than ONE_HOUR_FROM_NOW + const pttl = pttlResults[i] + // A TTL of -1 means the entry was not configured with any TTL (expiration) + // currently and will remain as a permanent entry unless a TTL is added + const needsShortenedTtl = pttl === -1 || pttl > expirationDuration const isOldKey = !HEROKU_RELEASE_VERSION || !key.startsWith(`${HEROKU_RELEASE_VERSION}:`) if (needsShortenedTtl && isOldKey) { @@ -112,17 +156,21 @@ function purgeRenderedPageCache () { } }) - // Only update TTLs if there are records worth updating - if (updatingKeyCount > 0) { - // Set all the TTLs - const pexpireAtResults = await pexpireAtPipeline.exec() - const updatedResults = pexpireAtResults.filter(([error, result]) => error == null && result === 1) + console.log(`Purging ${updatingKeyCount} keys...`) - // Count only the entries whose TTLs were successfully updated - totalKeyCount += updatedResults.length + // Only update TTLs if there are records worth updating + if (updatingKeyCount === 0) return + + // Set all the TTLs + const pexpireAtPipelineExecAsync = promisify(pexpireAtPipeline.exec).bind(pexpireAtPipeline) + const pexpireAtResults = await pexpireAtPipelineExecAsync() + + if (pttlResults == null || pttlResults.length === 0) { + throw new Error('PEXPIREAT results were empty') } - // Resume the SCAN stream - scanStream.resume() - }) + // Count only the entries whose TTLs were successfully updated + const updatedResults = pexpireAtResults.filter((result) => result === 1) + return updatedResults.length + } }