Repository: cli/cli

Issue:
Automatically update third party licenses during Dependabot PRs
## Overview

With `cli/cli` lint process erring if 3rd party license information is not updated in https://github.com/cli/cli/pull/11047, Dependabot PRs will require maintainers to manually run `make licenses`.

Recently, @williammartin opened https://github.com/cli/cli/pull/11269 with the [`script/fix-dependabot-licenses.sh`](https://github.com/cli/cli/blob/26d70bfb7bcc0b41dbdd50bfc51f827f1a5ad4c4/script/fix-dependabot-licenses.sh) script for maintainers to run that will find all Dependabot PRs and attempt to fix them where the lint workflow failed.  This script is a manual repair effort, however it is possible to [use a GitHub Actions workflow to run the `make license` script for Dependabot PRs](https://docs.github.com/en/code-security/dependabot/working-with-dependabot/automating-dependabot-with-github-actions):

> ```yaml
> name: Dependabot fetch metadata
> on: pull_request
> 
> permissions:
>   pull-requests: write
>   issues: write
> 
> jobs:
>   dependabot:
>     runs-on: ubuntu-latest
>     if: github.event.pull_request.user.login == 'dependabot[bot]' && github.repository == 'owner/my_repo'
>     steps:
>       - name: Dependabot metadata
>         id: metadata
>         uses: dependabot/fetch-metadata@d7267f607e9d3fb96fc2fbe83e0af444713e90b7
>         with:
>           github-token: "${{ secrets.GITHUB_TOKEN }}"
>       # The following properties are now available:
>       #  - steps.metadata.outputs.dependency-names
>       #  - steps.metadata.outputs.dependency-type
>       #  - steps.metadata.outputs.update-type
> ```

This issue is aimed at implementing GitHub Actions workflow changes that will automatically update `third-party` license source code and `third-party-*.md` reports, eliminating the need for maintainers to manually repair Dependabot PRs.

> [!NOTE]
> To download the `script/fix-dependabot-licenses.sh` script, run the following command:
> ```shell
> curl -o fix-dependabot-licenses.sh https://raw.githubusercontent.com/cli/cli/26d70bfb7bcc0b41dbdd50bfc51f827f1a5ad4c4/script/fix-dependabot-licenses.sh
> ```
>
> Or checkout the original PR:
>
> ```shell
> gh pr checkout https://github.com/cli/cli/pull/11269
> ```

## Expected outcomes

- When Dependabot PRs are opened, automation attempts to regenerate and commit updated license information via `make licenses`
- When Dependabot PRs are updated, status checks pass without maintainer action outside of reviewing PR

Hints from issue comments:
@andyfeller: One of the concerns raised that might prevent this from being automatically fixed is how GitHub Actions status checks are triggered by pushes using the `secrets.GITHUB_TOKEN` automatic token.

This concern is related to [Triggering a workflow from a workflow](https://docs.github.com/en/actions/how-tos/writing-workflows/choosing-when-your-workflow-runs/triggering-a-workflow#triggering-a-workflow-from-a-workflow) where GitHub Actions might not automatically re-run the status checks on the original PR for changes made by a different workflow:

> When you use the repository's GITHUB_TOKEN to perform tasks, events triggered by the GITHUB_TOKEN, with the exception of workflow_dispatch and repository_dispatch, will not create a new workflow run. This prevents you from accidentally creating recursive workflow runs. For example, if a workflow run pushes code using the repository's GITHUB_TOKEN, a new workflow will not run even when the repository contains a workflow configured to run when push events occur. For more information, see [Use GITHUB_TOKEN in workflows](https://docs.github.com/en/actions/security-guides/automatic-token-authentication).

In the event we cannot automatically fix this as described, then we will reopen and merge @williammartin work in https://github.com/cli/cli/pull/11269 as to reduce maintainer toil.

Solution patch:
diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml
index 5281a46d0..5afe6cd2f 100644
--- a/.github/workflows/lint.yml
+++ b/.github/workflows/lint.yml
@@ -7,15 +7,11 @@ on:
       - "**.go"
       - go.mod
       - go.sum
-      - ".github/licenses.tmpl"
-      - "script/licenses*"
   pull_request:
     paths:
       - "**.go"
       - go.mod
       - go.sum
-      - ".github/licenses.tmpl"
-      - "script/licenses*"
 permissions:
   contents: read
 jobs:
@@ -50,18 +46,6 @@ jobs:
         with:
           version: v2.1.6
 
-      # actions/setup-go does not setup the installed toolchain to be preferred over the system install,
-      # which causes go-licenses to raise "Package ... does not have module info" errors.
-      # for more information, https://github.com/google/go-licenses/issues/244#issuecomment-1885098633
-      #
-      # go-licenses has been pinned for automation use.
-      - name: Check licenses
-        run: |
-          export GOROOT=$(go env GOROOT)
-          export PATH=${GOROOT}/bin:$PATH
-          go install github.com/google/go-licenses@5348b744d0983d85713295ea08a20cca1654a45e
-          make licenses-check
-
   # Discover vulnerabilities within Go standard libraries used to build GitHub CLI using govulncheck.
   govulncheck:
     runs-on: ubuntu-latest
diff --git a/.github/workflows/scripts/check-help-wanted.sh b/.github/workflows/scripts/check-help-wanted.sh
index 75462ddb9..d713be144 100755
--- a/.github/workflows/scripts/check-help-wanted.sh
+++ b/.github/workflows/scripts/check-help-wanted.sh
@@ -56,7 +56,7 @@ for issue_num in $CLOSING_ISSUES; do
     fi
 
     # Check if 'help wanted' label exists
-    if ! echo "$LABELS" | grep -q "help wanted"; then
+    if ! echo "$LABELS" | grep -qE '^help wanted$'; then
         ISSUES_WITHOUT_HELP_WANTED+=("$issue_num")
         echo "Issue #$issue_num does not have 'help wanted' label"
     else
@@ -78,7 +78,7 @@ if [ ${#ISSUES_WITHOUT_HELP_WANTED[@]} -gt 0 ]; then
     gh pr comment "$PR_URL" --body-file - <<EOF
 Thank you for your pull request! 🎉
 
-This PR appears to fix the following issues that are not labeled with \`help wanted\`:
+This PR appears to fix the following issues that are not labeled with https://github.com/cli/cli/labels/help%20wanted:
 
 $ISSUE_LIST
 As outlined in our [Contributing Guidelines](https://github.com/cli/cli/blob/trunk/.github/CONTRIBUTING.md), we expect that PRs are only created for issues that have been labeled \`help wanted\`.
diff --git a/.github/workflows/scripts/spam-detection/eval-prompts.yml b/.github/workflows/scripts/spam-detection/eval-prompts.yml
index be7efddfc..15c61ff76 100644
--- a/.github/workflows/scripts/spam-detection/eval-prompts.yml
+++ b/.github/workflows/scripts/spam-detection/eval-prompts.yml
@@ -10,126 +10,6 @@ evaluators:
     string:
       equals: "{{expected}}"
 testData:
-  - name: not-spam, staff issue
-    expected: PASS
-    input: |
-      <TITLE>
-      Automatically update third party licenses during Dependabot PRs
-      </TITLE>
-
-      <BODY>
-      ## Overview
-
-      With `cli/cli` lint process erring if 3rd party license information is not updated in https://github.com/cli/cli/pull/11047, Dependabot PRs will require maintainers to manually run `make licenses`.
-
-      Recently, @williammartin opened https://github.com/cli/cli/pull/11269 with the [`script/fix-dependabot-licenses.sh`](https://github.com/cli/cli/blob/26d70bfb7bcc0b41dbdd50bfc51f827f1a5ad4c4/script/fix-dependabot-licenses.sh) script for maintainers to run that will find all Dependabot PRs and attempt to fix them where the lint workflow failed.  This script is a manual repair effort, however it is possible to [use a GitHub Actions workflow to run the `make license` script for Dependabot PRs](https://docs.github.com/en/code-security/dependabot/working-with-dependabot/automating-dependabot-with-github-actions):
-
-      > ```yaml
-      > name: Dependabot fetch metadata
-      > on: pull_request
-      >
-      > permissions:
-      >   pull-requests: write
-      >   issues: write
-      >
-      > jobs:
-      >   dependabot:
-      >     runs-on: ubuntu-latest
-      >     if: github.event.pull_request.user.login == 'dependabot[bot]' && github.repository == 'owner/my_repo'
-      >     steps:
-      >       - name: Dependabot metadata
-      >         id: metadata
-      >         uses: dependabot/fetch-metadata@d7267f607e9d3fb96fc2fbe83e0af444713e90b7
-      >         with:
-      >           github-token: "${{ secrets.GITHUB_TOKEN }}"
-      >       # The following properties are now available:
-      >       #  - steps.metadata.outputs.dependency-names
-      >       #  - steps.metadata.outputs.dependency-type
-      >       #  - steps.metadata.outputs.update-type
-      > ```
-
-      This issue is aimed at implementing GitHub Actions workflow changes that will automatically update `third-party` license source code and `third-party-*.md` reports, eliminating the need for maintainers to manually repair Dependabot PRs.
-
-      > [!NOTE]
-      > To download the `script/fix-dependabot-licenses.sh` script, run the following command:
-      > ```shell
-      > curl -o fix-dependabot-licenses.sh https://raw.githubusercontent.com/cli/cli/26d70bfb7bcc0b41dbdd50bfc51f827f1a5ad4c4/script/fix-dependabot-licenses.sh
-      > ```
-      >
-      > Or checkout the original PR:
-      >
-      > ```shell
-      > gh pr checkout https://github.com/cli/cli/pull/11269
-      > ```
-
-      ## Expected outcomes
-
-      - When Dependabot PRs are opened, automation attempts to regenerate and commit updated license information via `make licenses`
-      - When Dependabot PRs are updated, status checks pass without maintainer action outside of reviewing PR
-          - name: not-spam, template-based
-          spam: true
-          title: |
-              Incorrect check summary with v2.45 and v2.75
-          body: |
-              ### Describe the bug
-
-              I got below confusing reports with v2.45 CLI on Ubuntu 22.04:
-
-              ```sh
-              $ gh pr status
-
-              Relevant pull requests in micropython/micropython
-              ...
-              Created by you
-              #17660  tests/extmod: Close UDP timely. [yf13:pull-udp-close]
-              ✓ Checks passing
-              #17638  unix/make: Drop i686-linux-gnu path. [yf13:pull-drop-i686-linux-gnu]
-              × 1/94 checks failing
-              $ gh pr checks 17638
-              All checks were successful
-              0 cancelled, 0 failing, 48 successful, 0 skipped, and 0 pending checks
-              ```
-              I downloaded latest v2.75 CLI but it is the same.
-
-              Meanwhile. from browser UI it seems checks passed.
-
-              ### Affected version
-
-              Please run `gh version` and paste the output below.
-
-              ```
-              $ gh version
-              gh version 2.75.0 (2025-07-09)
-              https://github.com/cli/cli/releases/tag/v2.75.0
-              ```
-
-              ### Steps to reproduce the behavior
-
-              see above descriptions.
-
-              ### Expected vs actual behavior
-
-              A clear and concise description of what you expected to happen and what actually happened.
-
-              ### Logs
-
-              Paste the activity from your command line. Redact if needed.
-
-              <!-- Note: Set `GH_DEBUG=true` for verbose logs or `GH_DEBUG=api` for verbose logs with HTTP traffic details. -->
-
-      </BODY>
-  - name: not-spam, short/focused
-    expected: PASS
-    input: |
-      <TITLE>
-      Include `isImmutable` in `release list`
-      </TITLE>
-
-      <BODY>
-      Update the list of available JSON fields in the `release list` command to include `isImmutable` flag.
-
-      This boolean flag indicates whether a particular release has been marked as immutable.
-      </BODY>
   - name: spam, two words
     expected: FAIL
     input: |
@@ -295,34 +175,6 @@ testData:
 
       Add any other context like screenshots or mockups are helpful, if applicable.
       </BODY>
-  - name: 'spam, legit but too general #10368 (https://github.com/cli/cli/issues/10368)'
-    expected: FAIL
-    input: |-
-      <TITLE>
-      Instructions in install_linux.md do not result in installation
-      </TITLE>
-
-      <BODY>
-      ### Describe the bug
-
-      Bug: the instructions meant to install gh instead don't install gh.
-
-      ### Affected version
-
-      Latest
-
-      ### Steps to reproduce the behavior
-
-      Follow instructions in install_linux.md
-
-      ### Expected vs actual behavior
-
-      Expect: gh is installed and can be used.
-
-      ### Logs
-
-      A bunch of errors
-      </BODY>
   - name: 'spam, #11304 (https://github.com/cli/cli/issues/11304)'
     expected: FAIL
     input: |-
@@ -820,6 +672,106 @@ testData:
   - name: 'spam, #9928 (https://github.com/cli/cli/issues/9928)'
     expected: FAIL
     input: "<TITLE>\nNote that an earlier version of the instructions used the location `/usr/share/keyrings` instead of `/etc/apt/keyrings` in the `sources.list.d` file, so I had to update that to make it work with the above update instructions, and remove the old keyring file from `/usr/share/keyrings`.\n</TITLE>\n\n<BODY>\n              Note that an earlier version of the instructions used the location `/usr/share/keyrings` instead of `/etc/apt/keyrings` in the `sources.list.d` file, so I had to update that to make it work with the above update instructions, and remove the old keyring file from `/usr/share/keyrings`.\r\n\r\nAlternatively, one could of course download the updated key to `/usr/share/keyrings`, but we don't really want to pollute `/usr` with non-packaged files!\r\n\r\n_Originally posted by @rrthomas in https://github.com/cli/cli/issues/9569#issuecomment-2333981674_\r\n            \n</BODY>"
+  - name: 'spam, #10075 (https://github.com/cli/cli/issues/10075)'
+    expected: FAIL
+    input: "<TITLE>\nRHEL 9 installation update\n</TITLE>\n\n<BODY>\n### Describe the bug\r\n\r\nsteps to install on RHEL9 \r\n\r\n### Steps to reproduce the behavior\r\n\r\n\r\n### Expected vs actual behavior\r\n\r\n```\r\nsudo dnf install dnf-plugins-core.noarch\r\nsudo dnf config-manager --add-repo https://cli.github.com/packages/rpm/gh-cli.repo\r\nsudo dnf install gh --repo gh-cli\r\n```\n</BODY>"
+  - name: not spam, staff issue
+    expected: PASS
+    input: |
+      <TITLE>
+      Automatically update third party licenses during Dependabot PRs
+      </TITLE>
+
+      <BODY>
+      ## Overview
+
+      With `cli/cli` lint process erring if 3rd party license information is not updated in https://github.com/cli/cli/pull/11047, Dependabot PRs will require maintainers to manually run `make licenses`.
+
+      Recently, @williammartin opened https://github.com/cli/cli/pull/11269 with the [`script/fix-dependabot-licenses.sh`](https://github.com/cli/cli/blob/26d70bfb7bcc0b41dbdd50bfc51f827f1a5ad4c4/script/fix-dependabot-licenses.sh) script for maintainers to run that will find all Dependabot PRs and attempt to fix them where the lint workflow failed.  This script is a manual repair effort, however it is possible to [use a GitHub Actions workflow to run the `make license` script for Dependabot PRs](https://docs.github.com/en/code-security/dependabot/working-with-dependabot/automating-dependabot-with-github-actions):
+
+      > ```yaml
+      > name: Dependabot fetch metadata
+      > on: pull_request
+      >
+      > permissions:
+      >   pull-requests: write
+      >   issues: write
+      >
+      > jobs:
+      >   dependabot:
+      >     runs-on: ubuntu-latest
+      >     if: github.event.pull_request.user.login == 'dependabot[bot]' && github.repository == 'owner/my_repo'
+      >     steps:
+      >       - name: Dependabot metadata
+      >         id: metadata
+      >         uses: dependabot/fetch-metadata@d7267f607e9d3fb96fc2fbe83e0af444713e90b7
+      >         with:
+      >           github-token: "${{ secrets.GITHUB_TOKEN }}"
+      >       # The following properties are now available:
+      >       #  - steps.metadata.outputs.dependency-names
+      >       #  - steps.metadata.outputs.dependency-type
+      >       #  - steps.metadata.outputs.update-type
+      > ```
+
+      This issue is aimed at implementing GitHub Actions workflow changes that will automatically update `third-party` license source code and `third-party-*.md` reports, eliminating the need for maintainers to manually repair Dependabot PRs.
+
+      > [!NOTE]
+      > To download the `script/fix-dependabot-licenses.sh` script, run the following command:
+      > ```shell
+      > curl -o fix-dependabot-licenses.sh https://raw.githubusercontent.com/cli/cli/26d70bfb7bcc0b41dbdd50bfc51f827f1a5ad4c4/script/fix-dependabot-licenses.sh
+      > ```
+      >
+      > Or checkout the original PR:
+      >
+      > ```shell
+      > gh pr checkout https://github.com/cli/cli/pull/11269
+      > ```
+
+      ## Expected outcomes
+
+      - When Dependabot PRs are opened, automation attempts to regenerate and commit updated license information via `make licenses`
+      - When Dependabot PRs are updated, status checks pass without maintainer action outside of reviewing PR
+      </BODY>
+  - name: not spam, short/focused
+    expected: PASS
+    input: |
+      <TITLE>
+      Include `isImmutable` in `release list`
+      </TITLE>
+
+      <BODY>
+      Update the list of available JSON fields in the `release list` command to include `isImmutable` flag.
+
+      This boolean flag indicates whether a particular release has been marked as immutable.
+      </BODY>
+  - name: 'not spam, legit but too general #10368 (https://github.com/cli/cli/issues/10368)'
+    expected: PASS
+    input: |-
+      <TITLE>
+      Instructions in install_linux.md do not result in installation
+      </TITLE>
+
+      <BODY>
+      ### Describe the bug
+
+      Bug: the instructions meant to install gh instead don't install gh.
+
+      ### Affected version
+
+      Latest
+
+      ### Steps to reproduce the behavior
+
+      Follow instructions in install_linux.md
+
+      ### Expected vs actual behavior
+
+      Expect: gh is installed and can be used.
+
+      ### Logs
+
+      A bunch of errors
+      </BODY>
   - name: 'not spam, #11277 (https://github.com/cli/cli/issues/11277)'
     expected: PASS
     input: |-
@@ -4482,9 +4434,6 @@ testData:
   - name: 'not spam, #10076 (https://github.com/cli/cli/issues/10076)'
     expected: PASS
     input: "<TITLE>\n`gh run list` does not work with organization ruleset required workflows\n</TITLE>\n\n<BODY>\n### Describe the bug\r\n\r\nSimilar bug mentioned https://github.com/cli/cli/issues/3437, but gh run view or list all return a 404. The URL returned seems right based on REST api docs but not getting any response. When comparing the ID `gh run list` doesn't seem to be correct based on the ids from `gh workflow list`\r\n\r\n**gh cli version:** `gh version 2.63.2 (2024-12-05)`\r\n**ghe version:** `3.13.4`\r\n\r\n### Steps to reproduce the behavior\r\n\r\n1. Complete login to the enterprise server with Github CLI\r\n2. Go to a repository directory that uses that server as a remote\r\n3. Run gh run list returns 404\r\n\r\n### Expected vs actual behavior\r\n\r\nThe gh run list prints out list of workflow runs for the repo to choose from\r\n\r\n### Logs\r\n\r\n```bash\r\n[git remote -v]\r\n[git config --get-regexp ^remote\\..*\\.gh-resolved$]\r\n* Request at 2024-12-13 00:23:19.723417 -0600 CST m=+0.101249251\r\n* Request to https://{SERVER_URL}/api/graphql\r\n* Request took 281.385ms\r\n⣾* Request at 2024-12-13 00:23:20.040818 -0600 CST m=+0.418510918\r\n* Request to https://{SERVER_URL}/api/v3/repos/{ORG}/{REPO}/actions/runs?per_page=20&exclude_pull_requests=true\r\n⢿* Request took 421.362291ms\r\n⡿* Request at 2024-12-13 00:23:20.534045 -0600 CST m=+0.911535293\r\n* Request to https://{SERVER_URL}/api/v3/repos/{ORG}/{REPO}/actions/workflows?per_page=100&page=1\r\n⣟* Request took 105.218541ms\r\n* Request at 2024-12-13 00:23:20.700194 -0600 CST m=+1.077616418\r\n* Request to https://{SERVER_URL}/api/v3/repos/{ORG}/{REPO}/actions/workflows/63737\r\n⣯* Request took 121.476458ms\r\nfailed to get runs: HTTP 404: Not Found (https://{SERVER_URL}/api/v3/repos/{ORG}/{REPO}/actions/workflows/63737)\r\n```\r\n</BODY>"
-  - name: 'not spam, #10075 (https://github.com/cli/cli/issues/10075)'
-    expected: PASS
-    input: "<TITLE>\nRHEL 9 installation update\n</TITLE>\n\n<BODY>\n### Describe the bug\r\n\r\nsteps to install on RHEL9 \r\n\r\n### Steps to reproduce the behavior\r\n\r\n\r\n### Expected vs actual behavior\r\n\r\n```\r\nsudo dnf install dnf-plugins-core.noarch\r\nsudo dnf config-manager --add-repo https://cli.github.com/packages/rpm/gh-cli.repo\r\nsudo dnf install gh --repo gh-cli\r\n```\n</BODY>"
   - name: 'not spam, #10073 (https://github.com/cli/cli/issues/10073)'
     expected: PASS
     input: "<TITLE>\n`gh gist delete` does not prompt for a gist to delete or prompt for confirmation before deletion\n</TITLE>\n\n<BODY>\n### Describe the bug\n\n- `gh gist delete` doesn't prompt for a gist to delete. This seems like it might be an oversight when compared to the behavior of other `gh gist` and `gh <some-command> delete` operations.\n- `gh gist delete` should prompt for a gist to delete and confirm the selection to delete.\n- `gh gist delete` also does not currently support `--yes` for non-interactive confirmation - perhaps it should? \n\n### Steps to reproduce the behavior\n\n```\ngh gist delete\n```\n### Expected vs actual behavior\n\n**Expected**\n\n```\n❯ gh gist delete\n? Select a gist to delete [Use arrows to move, type to filter]\n> test.md test gist about 4 days ago\n  draft.md  about 2 months ago\n? Are you sure you want to delete gist test.md (Y/n)\n```\n\n**Actual**\n```\ngh gist delete\n❯ gh gist delete\ncannot delete: gist argument required\n\nUsage:  gh gist delete {<id> | <url>} [flags]\n```\n\n### Notes\n\nDiscovered in #10042 \n</BODY>"
diff --git a/.github/workflows/scripts/spam-detection/eval.sh b/.github/workflows/scripts/spam-detection/eval.sh
index 4bf8b08fe..2a0b93d4c 100755
--- a/.github/workflows/scripts/spam-detection/eval.sh
+++ b/.github/workflows/scripts/spam-detection/eval.sh
@@ -14,8 +14,4 @@ SPAM_DIR="$(dirname "$(realpath "$0")")"
 _system_prompt="$($SPAM_DIR/generate-sys-prompt.sh)"
 _final_prompt="$(_value="$_system_prompt" yq eval '.messages[0].content = strenv(_value)' $SPAM_DIR/eval-prompts.yml)"
 
-# The following `gh models eval` command will fail after 20 requests due to rate limits.
-# We are going to open up an issue in `github/gh-models` to address this.
-#
-# TODO: break up `eval-prompts.yml` file into smaller batches to avoid hitting the rate limit.
 gh models eval <(echo "$_final_prompt")
diff --git a/.github/workflows/third-party-licenses.yml b/.github/workflows/third-party-licenses.yml
new file mode 100644
index 000000000..3cdc5858e
--- /dev/null
+++ b/.github/workflows/third-party-licenses.yml
@@ -0,0 +1,52 @@
+name: Third Party Licenses
+on:
+  push:
+    branches:
+      - trunk
+    paths:
+      - .github/licenses.tmpl
+      - .github/workflows/third-party-licenses.yml
+      - go.mod
+      - go.sum
+      - script/licenses*
+jobs:
+  # This job is responsible for updating the third-party license reports and source code.
+  # It should be safe to cancel as the latest version of `go.mod` should be checked in.
+  regenerate-licenses:
+    runs-on: ubuntu-latest
+    concurrency:
+      group: ${{ github.workflow }}
+      cancel-in-progress: true
+    permissions:
+      contents: write
+    steps:
+      - name: Check out code
+        uses: actions/checkout@v4
+        with:
+          ref: trunk
+
+      - name: Set up Go
+        uses: actions/setup-go@v5
+        with:
+          go-version-file: 'go.mod'
+
+      - name: Regenerate licenses
+        run: |
+          export GOROOT=$(go env GOROOT)
+          export PATH=${GOROOT}/bin:$PATH
+          go install github.com/google/go-licenses@5348b744d0983d85713295ea08a20cca1654a45e
+          make licenses
+          git diff
+
+      - name: Commit and push changes
+        run: |
+          if git diff --exit-code; then
+            echo "No third-party license changes to commit"
+          else
+            git config --local user.name "github-actions[bot]"
+            git config --local user.email "41898282+github-actions[bot]@users.noreply.github.com"
+            git add third-party third-party-licenses.*.md
+            git commit -m "Generate licenses - $GITHUB_SHA"
+            git pull
+            git push origin
+          fi
diff --git a/pkg/cmd/pr/shared/completion.go b/pkg/cmd/pr/shared/completion.go
index c1296be71..9f68c0bda 100644
--- a/pkg/cmd/pr/shared/completion.go
+++ b/pkg/cmd/pr/shared/completion.go
@@ -14,7 +14,10 @@ import (
 func RequestableReviewersForCompletion(httpClient *http.Client, repo ghrepo.Interface) ([]string, error) {
 	client := api.NewClientFromHTTP(api.NewCachedHTTPClient(httpClient, time.Minute*2))
 
-	metadata, err := api.RepoMetadata(client, repo, api.RepoMetadataInput{Reviewers: true})
+	metadata, err := api.RepoMetadata(client, repo, api.RepoMetadataInput{
+		Reviewers:     true,
+		TeamReviewers: true,
+	})
 	if err != nil {
 		return nil, err
 	}
diff --git a/pkg/cmd/pr/shared/survey.go b/pkg/cmd/pr/shared/survey.go
index af1fa871a..4b66bb0fa 100644
--- a/pkg/cmd/pr/shared/survey.go
+++ b/pkg/cmd/pr/shared/survey.go
@@ -181,6 +181,7 @@ func MetadataSurvey(p Prompt, io *iostreams.IOStreams, baseRepo ghrepo.Interface
 	// Retrieve and process data for survey prompts based on the extra fields selected
 	metadataInput := api.RepoMetadataInput{
 		Reviewers:      isChosen("Reviewers"),
+		TeamReviewers:  isChosen("Reviewers"),
 		Assignees:      isChosen("Assignees"),
 		ActorAssignees: isChosen("Assignees") && state.ActorAssignees,
 		Labels:         isChosen("Labels"),



Test patch:
diff --git a/pkg/cmd/pr/create/create_test.go b/pkg/cmd/pr/create/create_test.go
index 469428670..9af351830 100644
--- a/pkg/cmd/pr/create/create_test.go
+++ b/pkg/cmd/pr/create/create_test.go
@@ -1655,7 +1655,7 @@ func Test_createRun(t *testing.T) {
 			expectedOut: "https://github.com/OWNER/REPO/pull/12\n",
 		},
 		{
-			name: "if reviewer contains any team, fetch teams",
+			name: "fetch org teams non-interactively if reviewer contains any team",
 			setup: func(opts *CreateOptions, t *testing.T) func() {
 				opts.TitleProvided = true
 				opts.BodyProvided = true
@@ -1718,7 +1718,7 @@ func Test_createRun(t *testing.T) {
 			expectedErrOut: "",
 		},
 		{
-			name: "if reviewer does NOT contain any team, do NOT fetch teams",
+			name: "do not fetch org teams non-interactively if reviewer does not contain any team",
 			setup: func(opts *CreateOptions, t *testing.T) func() {
 				opts.TitleProvided = true
 				opts.BodyProvided = true
@@ -1773,6 +1773,130 @@ func Test_createRun(t *testing.T) {
 			expectedOut:    "https://github.com/OWNER/REPO/pull/12\n",
 			expectedErrOut: "",
 		},
+		{
+			name: "fetch org teams interactively if reviewer metadata selected",
+			tty:  true,
+			setup: func(opts *CreateOptions, t *testing.T) func() {
+				// In order to test additional metadata, title and body cannot be provided here.
+				opts.HeadBranch = "feature"
+				return func() {}
+			},
+			cmdStubs: func(cs *run.CommandStubber) {
+				// Stub git commits for `initDefaultTitleBody` when initializing PR state.
+				cs.Register(
+					"git -c log.ShowSignature=false log --pretty=format:%H%x00%s%x00%b%x00 --cherry origin/master...feature",
+					0,
+					"3a9b48085046d156c5acce8f3b3a0532cd706a4a\u0000first commit of pr\u0000first commit description\u0000\n",
+				)
+				cs.Register(`git rev-parse --show-toplevel`, 0, "")
+			},
+			promptStubs: func(pm *prompter.PrompterMock) {
+				firstConfirmSubmission := true
+				pm.InputFunc = func(message, defaultValue string) (string, error) {
+					switch message {
+					case "Title (required)":
+						return "TITLE", nil
+					default:
+						return "", fmt.Errorf("unexpected input prompt: %s", message)
+					}
+				}
+				pm.MarkdownEditorFunc = func(message, defaultValue string, allowEmpty bool) (string, error) {
+					switch message {
+					case "Body":
+						return "BODY", nil
+					default:
+						return "", fmt.Errorf("unexpected markdown editor prompt: %s", message)
+					}
+				}
+				pm.MultiSelectFunc = func(message string, defaults []string, options []string) ([]int, error) {
+					switch message {
+					case "What would you like to add?":
+						return prompter.IndexesFor(options, "Reviewers")
+					case "Reviewers":
+						return prompter.IndexesFor(options, "MonaLisa (Mona Display Name)", "OWNER/core")
+					default:
+						return nil, fmt.Errorf("unexpected multi-select prompt: %s", message)
+					}
+				}
+				pm.SelectFunc = func(message, defaultValue string, options []string) (int, error) {
+					switch message {
+					case "Where should we push the 'feature' branch?":
+						return 0, nil
+					case "What's next?":
+						if firstConfirmSubmission {
+							firstConfirmSubmission = false
+							return prompter.IndexFor(options, "Add metadata")
+						}
+						return prompter.IndexFor(options, "Submit")
+					default:
+						return 0, fmt.Errorf("unexpected select prompt: %s", message)
+					}
+				}
+			},
+			httpStubs: func(reg *httpmock.Registry, t *testing.T) {
+				reg.Register(
+					httpmock.GraphQL(`query UserCurrent\b`),
+					httpmock.StringResponse(`{"data": {"viewer": {"login": "OWNER"} } }`))
+				reg.Register(
+					httpmock.GraphQL(`query PullRequestTemplates\b`),
+					httpmock.StringResponse(`{ "data": { "repository": { "pullRequestTemplates": [] } } }`),
+				)
+				reg.Register(
+					httpmock.GraphQL(`query RepositoryAssignableUsers\b`),
+					httpmock.StringResponse(`
+					{ "data": { "repository": { "assignableUsers": {
+						"nodes": [
+							{ "login": "hubot", "id": "HUBOTID", "name": "" },
+							{ "login": "MonaLisa", "id": "MONAID", "name": "Mona Display Name" }
+						],
+						"pageInfo": { "hasNextPage": false }
+					} } } }
+					`))
+				reg.Register(
+					httpmock.GraphQL(`query OrganizationTeamList\b`),
+					httpmock.StringResponse(`
+					{ "data": { "organization": { "teams": {
+						"nodes": [
+							{ "slug": "core", "id": "COREID" },
+							{ "slug": "robots", "id": "ROBOTID" }
+						],
+						"pageInfo": { "hasNextPage": false }
+					} } } }
+					`))
+				reg.Register(
+					httpmock.GraphQL(`mutation PullRequestCreate\b`),
+					httpmock.GraphQLMutation(`
+						{ "data": { "createPullRequest": { "pullRequest": {
+							"id": "NEWPULLID",
+							"URL": "https://github.com/OWNER/REPO/pull/12"
+						} } } }
+						`,
+						func(inputs map[string]interface{}) {
+							assert.Equal(t, "TITLE", inputs["title"])
+							assert.Equal(t, "BODY", inputs["body"])
+							if v, ok := inputs["assigneeIds"]; ok {
+								t.Errorf("did not expect assigneeIds: %v", v)
+							}
+							if v, ok := inputs["userIds"]; ok {
+								t.Errorf("did not expect userIds: %v", v)
+							}
+						}))
+				reg.Register(
+					httpmock.GraphQL(`mutation PullRequestCreateRequestReviews\b`),
+					httpmock.GraphQLMutation(`
+					{ "data": { "requestReviews": {
+						"clientMutationId": ""
+					} } }
+				`, func(inputs map[string]interface{}) {
+						assert.Equal(t, "NEWPULLID", inputs["pullRequestId"])
+						assert.Equal(t, []interface{}{"COREID"}, inputs["teamIds"])
+						assert.Equal(t, []interface{}{"MONAID"}, inputs["userIds"])
+						assert.Equal(t, true, inputs["union"])
+					}))
+			},
+			expectedOut:    "https://github.com/OWNER/REPO/pull/12\n",
+			expectedErrOut: "\nCreating pull request for feature into master in OWNER/REPO\n\n",
+		},
 	}
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
diff --git a/pkg/cmd/pr/shared/completion_test.go b/pkg/cmd/pr/shared/completion_test.go
new file mode 100644
index 000000000..ca7c3ffa7
--- /dev/null
+++ b/pkg/cmd/pr/shared/completion_test.go
@@ -0,0 +1,120 @@
+package shared
+
+import (
+	"net/http"
+	"testing"
+
+	"github.com/cli/cli/v2/internal/ghrepo"
+	"github.com/cli/cli/v2/pkg/httpmock"
+	"github.com/stretchr/testify/require"
+)
+
+func TestRequestableReviewersForCompletion(t *testing.T) {
+	tests := []struct {
+		name              string
+		expectedReviewers []string
+		httpStubs         func(*httpmock.Registry, *testing.T)
+	}{
+		{
+			name:              "when users and teams are both available, both are listed",
+			expectedReviewers: []string{"MonaLisa\tMona Display Name", "OWNER/core", "OWNER/robots", "hubot"},
+			httpStubs: func(reg *httpmock.Registry, t *testing.T) {
+				reg.Register(
+					httpmock.GraphQL(`query UserCurrent\b`),
+					httpmock.StringResponse(`{"data": {"viewer": {"login": "OWNER"} } }`))
+				reg.Register(
+					httpmock.GraphQL(`query RepositoryAssignableUsers\b`),
+					httpmock.StringResponse(`
+						{ "data": { "repository": { "assignableUsers": {
+							"nodes": [
+								{ "login": "hubot", "id": "HUBOTID", "name": "" },
+								{ "login": "MonaLisa", "id": "MONAID", "name": "Mona Display Name" }
+							],
+							"pageInfo": { "hasNextPage": false }
+						} } } }
+					`))
+				reg.Register(
+					httpmock.GraphQL(`query OrganizationTeamList\b`),
+					httpmock.StringResponse(`
+						{ "data": { "organization": { "teams": {
+							"nodes": [
+								{ "slug": "core", "id": "COREID" },
+								{ "slug": "robots", "id": "ROBOTID" }
+							],
+							"pageInfo": { "hasNextPage": false }
+						} } } }
+					`))
+			},
+		},
+		{
+			name:              "when users are available but teams aren't, users are listed",
+			expectedReviewers: []string{"MonaLisa\tMona Display Name", "hubot"},
+			httpStubs: func(reg *httpmock.Registry, t *testing.T) {
+				reg.Register(
+					httpmock.GraphQL(`query UserCurrent\b`),
+					httpmock.StringResponse(`{"data": {"viewer": {"login": "OWNER"} } }`))
+				reg.Register(
+					httpmock.GraphQL(`query RepositoryAssignableUsers\b`),
+					httpmock.StringResponse(`
+						{ "data": { "repository": { "assignableUsers": {
+							"nodes": [
+								{ "login": "hubot", "id": "HUBOTID", "name": "" },
+								{ "login": "MonaLisa", "id": "MONAID", "name": "Mona Display Name" }
+							],
+							"pageInfo": { "hasNextPage": false }
+						} } } }
+					`))
+				reg.Register(
+					httpmock.GraphQL(`query OrganizationTeamList\b`),
+					httpmock.StringResponse(`
+						{ "data": { "organization": { "teams": {
+							"nodes": [],
+							"pageInfo": { "hasNextPage": false }
+						} } } }
+					`))
+			},
+		},
+		{
+			name:              "when teams are available but users aren't, teams are listed",
+			expectedReviewers: []string{"OWNER/core", "OWNER/robots"},
+			httpStubs: func(reg *httpmock.Registry, t *testing.T) {
+				reg.Register(
+					httpmock.GraphQL(`query UserCurrent\b`),
+					httpmock.StringResponse(`{"data": {"viewer": {"login": "OWNER"} } }`))
+				reg.Register(
+					httpmock.GraphQL(`query RepositoryAssignableUsers\b`),
+					httpmock.StringResponse(`
+						{ "data": { "repository": { "assignableUsers": {
+							"nodes": [],
+							"pageInfo": { "hasNextPage": false }
+						} } } }
+					`))
+				reg.Register(
+					httpmock.GraphQL(`query OrganizationTeamList\b`),
+					httpmock.StringResponse(`
+						{ "data": { "organization": { "teams": {
+							"nodes": [
+								{ "slug": "core", "id": "COREID" },
+								{ "slug": "robots", "id": "ROBOTID" }
+							],
+							"pageInfo": { "hasNextPage": false }
+						} } } }
+					`))
+			},
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			reg := &httpmock.Registry{}
+			defer reg.Verify(t)
+			if tt.httpStubs != nil {
+				tt.httpStubs(reg, t)
+			}
+
+			reviewers, err := RequestableReviewersForCompletion(&http.Client{Transport: reg}, ghrepo.New("OWNER", "REPO"))
+			require.NoError(t, err)
+			require.Equal(t, tt.expectedReviewers, reviewers)
+		})
+	}
+}