Skip to content

Set GH_EXTENSION=1 when gh invokes an extension - #14072

Merged
williammartin merged 1 commit into
trunkfrom
williammartin-gh-extension-env-var
Aug 5, 2026
Merged

Set GH_EXTENSION=1 when gh invokes an extension#14072
williammartin merged 1 commit into
trunkfrom
williammartin-gh-extension-env-var

Conversation

@williammartin

@williammartin williammartin commented Aug 5, 2026

Copy link
Copy Markdown
Member

Closes #14071

Description

A gh extension is just an executable named gh-something. Once installed, it can be invoked two ways: as gh something, where gh finds it and runs it as a subprocess, or directly as ./gh-something, like any other program on your machine. Extension authors may want to tell these two cases apart, for example in providing usage strings: if someone runs gh label --help, the examples should read gh label list, but if they ran ./gh-label --help the examples should read ./gh-label list. Today the extension has no easy way to know which happened.

This adds a signal. When gh runs an extension, it now sets GH_EXTENSION=1 in that subprocess's environment. An extension checks for it and adjusts. Running the binary directly means no gh in the picture, so the variable is simply absent.

How did you test this change?

I wrote a throwaway extension that reports how it was invoked, then ran it both ways against a locally built gh:

Terminal recording: cat gh-whoami-am-i shows a script that branches on GH_EXTENSION. Running ./gh-whoami-am-i prints "I am running standalone. Usage: ./gh-whoami-am-i". Running gh whoami-am-i prints "I am running as a gh extension. Usage: gh whoami-am-i".

The same four behaviours are pinned down by a new acceptance script, acceptance/testdata/extension/extension-env.txtar.

Acceptance test run
$ GH_ACCEPTANCE_SCRIPT=extension-env.txtar \
  GH_ACCEPTANCE_HOST=github.com \
  GH_ACCEPTANCE_ORG=gh-acceptance-testing \
  GH_ACCEPTANCE_TOKEN=$(gh auth token) \
  go test -tags=acceptance -run '^TestExtensions$' -v ./acceptance
=== RUN   TestExtensions
=== RUN   TestExtensions/extension-env
=== PAUSE TestExtensions/extension-env
=== CONT  TestExtensions/extension-env
    testscript.go:584: WORK=$WORK

        [testscript environment preamble trimmed]

        SCRIPT_NAME=extension_env
        GH_CONFIG_DIR=$WORK
        GH_HOST=github.com
        ORG=gh-acceptance-testing
        RANDOM_STRING=wfmyBekyHn
        GH_TELEMETRY=false

        # Verify that gh tells an extension when it is being run as an extension
        # Skip if Bash is not available given script extension (0.000s)
        > [!exec:bash] skip
        # Setup environment variables used for testscript (0.000s)
        > env EXT_NAME=printenv-${RANDOM_STRING}
        > env EXT_DIR=gh-${EXT_NAME}
        # Setup a local extension that reports the value of GH_EXTENSION (0.002s)
        > mkdir $EXT_DIR
        > mv print-env.sh $EXT_DIR/$EXT_DIR
        > chmod 777 $EXT_DIR/$EXT_DIR
        # Install the local extension, gh extension install only supports the working directory (0.530s)
        > cd $EXT_DIR
        $WORK/gh-printenv-wfmyBekyHn
        > exec gh extension install .
        > defer gh extension remove $EXT_NAME
        # Verify GH_EXTENSION is set when the extension is run as gh <extension> (0.364s)
        > exec gh $EXT_NAME
        [stdout]
        GH_EXTENSION=1
        > stdout 'GH_EXTENSION=1'
        # Verify GH_EXTENSION is set when the extension is run via gh extension exec (0.055s)
        > exec gh extension exec $EXT_NAME
        [stdout]
        GH_EXTENSION=1
        > stdout 'GH_EXTENSION=1'
        # Verify GH_EXTENSION is absent when the extension is run standalone (0.014s)
        > exec ./$EXT_DIR
        [stdout]
        GH_EXTENSION=0
        > stdout 'GH_EXTENSION=0'
        # Verify GH_EXTENSION is documented (0.025s)
        > exec gh help environment
        [stdout]

        [preceding entries trimmed]

        `GH_NO_EXTENSION_UPDATE_NOTIFIER`: set to any value to disable GitHub CLI extension update notifications.
        When an extension is executed, gh checks for new versions for the executed extension once every 24 hours.
        If a newer version was found, an upgrade notice is displayed on standard error.

        `GH_EXTENSION`: set to `1` by gh when it invokes an extension, allowing an extension to
        tell whether it was run as `gh <extension>` or directly as a standalone program.

        `GH_CONFIG_DIR`: the directory where gh will store configuration files. If not specified,
        the default value will be one of the following paths (in order of precedence):
          - `$XDG_CONFIG_HOME/gh` (if `$XDG_CONFIG_HOME` is set),
          - `$AppData/GitHub CLI` (on Windows if `$AppData` is set), or
          - `$HOME/.config/gh`.

        [remaining entries trimmed]

        > stdout 'GH_EXTENSION`: set to `1` by gh when it invokes an extension'
        PASS

--- PASS: TestExtensions (0.00s)
    --- PASS: TestExtensions/extension-env (1.03s)
PASS
ok      github.com/cli/cli/v2/acceptance        1.615s

Key points

None

Notes for reviewers

Start with the acceptance test, and read it from top to bottom.

Authorship and follow-up

Who wrote this:

  • A human wrote it.
  • An agent wrote it under close human direction.
  • An agent wrote it independently, and no human has guided the implementation beyond the initial prompt.

Who answers review comments:

  • @williammartin will read and reply directly. Name the account.
  • An agent will draft replies and @username will read them before they are posted.
  • Nobody has explicitly committed to replying.

Copilot AI lite review requested due to automatic review settings August 5, 2026 09:11
@williammartin
williammartin requested a review from a team as a code owner August 5, 2026 09:11
@williammartin
williammartin requested a review from tidy-dev August 5, 2026 09:11

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This pull request adds a clear signal for extension authors to detect when their program is being invoked by gh (vs executed directly), by setting GH_EXTENSION=1 for all extension dispatch paths. It also documents the new environment variable and adds both unit and acceptance coverage to prevent regressions across OS-specific dispatch logic.

Changes:

  • Set GH_EXTENSION=1 in the environment for extension processes launched via Manager.Dispatch.
  • Document GH_EXTENSION in gh help environment.
  • Add unit tests for environment propagation/override and a new end-to-end acceptance script covering the required scenarios.
Show a summary per file
File Description
pkg/cmd/root/help_topic.go Documents the new GH_EXTENSION environment variable in gh help environment.
pkg/cmd/extension/manager.go Appends GH_EXTENSION=1 to the dispatched extension command’s environment.
pkg/cmd/extension/manager_test.go Adds regression tests verifying GH_EXTENSION is set, overrides inherited values, and preserves other env entries.
acceptance/testdata/extension/extension-env.txtar Adds an end-to-end acceptance script verifying GH_EXTENSION behavior for gh <ext>, gh extension exec, standalone execution, and documentation.

Review details

Tip

Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

  • Files reviewed: 4/4 changed files
  • Comments generated: 0
  • Review effort level: Lite

Co-authored-by: Copilot App <[email protected]>
Copilot-Session: cd6441de-bed4-4adb-88c3-904b349ba16f
@williammartin
williammartin force-pushed the williammartin-gh-extension-env-var branch from cd4b573 to 408534a Compare August 5, 2026 09:36
@williammartin
williammartin marked this pull request as ready for review August 5, 2026 09:36

@tidy-dev tidy-dev left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✨ Tested, works, makes sense.

@williammartin
williammartin merged commit 5ccd971 into trunk Aug 5, 2026
18 checks passed
@williammartin
williammartin deleted the williammartin-gh-extension-env-var branch August 5, 2026 11:52
tmeijn pushed a commit to tmeijn/dotfiles that referenced this pull request Aug 21, 2026
This MR contains the following updates:

| Package | Update | Change |
|---|---|---|
| [cli/cli](https://github.com/cli/cli) | minor | `v2.97.0` → `v2.98.0` |

MR created with the help of [el-capitano/tools/renovate-bot](https://gitlab.com/el-capitano/tools/renovate-bot).

**Proposed changes to behavior should be submitted there as MRs.**

---

### Release Notes

<details>
<summary>cli/cli (cli/cli)</summary>

### [`v2.98.0`](https://github.com/cli/cli/releases/tag/v2.98.0): GitHub CLI 2.98.0

[Compare Source](cli/cli@v2.97.0...v2.98.0)

#### Security

A security vulnerability has been identified, and fixed, that binds the local forwarded port to all available network interfaces by default.

Users of `gh codespace ports forward` are advised to update `gh` to version `v2.98.0` as soon as possible.

For more information see: <GHSA-vfhh-p7hm-pxfh>

#### Support worktrees in `pr checkout`

Users can now checkout a pull request into a git worktree by using the new `--worktree PATH` flag in `gh pr checkout`:

```shell
gh pr checkout 12 --worktree ../wt-feature
```

#### Add semantic search to `search issues`

The `gh search issues` command now supports semantic search for issues. Users can select the search type by passing the `--search-type` flag:

```shell
gh search issues --search-type semantic ...

gh search issues --search-type hybrid ...
```

For more information about semantic search see: ["Improved Search for github issues is now generally available"](https://github.blog/changelog/2026-04-02-improved-search-for-github-issues-is-now-generally-available/).

#### What's Changed

##### ✨ Features

- Add --worktree flag to gh pr checkout by [@&#8203;tidy-dev](https://github.com/tidy-dev) in [#&#8203;13946](cli/cli#13946)
- Set GH\_EXTENSION=1 when gh invokes an extension by [@&#8203;williammartin](https://github.com/williammartin) in [#&#8203;14072](cli/cli#14072)
- Add --search-type flag for semantic and hybrid issue search by [@&#8203;michaeljacholke](https://github.com/michaeljacholke) in [#&#8203;14006](cli/cli#14006)

##### 🐛 Fixes

- Fix `RESTWithNext` error type, repairing `gh status` and attestation retries by [@&#8203;williammartin](https://github.com/williammartin) in [#&#8203;13988](cli/cli#13988)
- Trim spaces when parsing X-Oauth-Scopes in `gh release create` by [@&#8203;williammartin](https://github.com/williammartin) in [#&#8203;14065](cli/cli#14065)
- Fix project item-add output for non-TTY by [@&#8203;zwick](https://github.com/zwick) in [#&#8203;14056](cli/cli#14056)

##### 📚 Docs & Chores

- Slim down dependabot triage comments by [@&#8203;williammartin](https://github.com/williammartin) in [#&#8203;14019](cli/cli#14019)
- Require explicit MR review ownership by [@&#8203;williammartin](https://github.com/williammartin) in [#&#8203;14028](cli/cli#14028)
- Collapse spam triage into the agentic issue-triage workflow by [@&#8203;williammartin](https://github.com/williammartin) in [#&#8203;14027](cli/cli#14027)
- Run Dependabot triage every hour by [@&#8203;sergiou87](https://github.com/sergiou87) in [#&#8203;14030](cli/cli#14030)
- Route deploy key requests through api.Client by [@&#8203;williammartin](https://github.com/williammartin) in [#&#8203;13989](cli/cli#13989)
- Route ssh key requests through api.Client by [@&#8203;williammartin](https://github.com/williammartin) in [#&#8203;13994](cli/cli#13994)
- Route gpg key requests through api.Client by [@&#8203;williammartin](https://github.com/williammartin) in [#&#8203;13997](cli/cli#13997)
- Route autolink requests through api.Client by [@&#8203;williammartin](https://github.com/williammartin) in [#&#8203;14013](cli/cli#14013)
- Route extension requests through api.Client by [@&#8203;williammartin](https://github.com/williammartin) in [#&#8203;14059](cli/cli#14059)
- Route release creation through api.Client by [@&#8203;williammartin](https://github.com/williammartin) in [#&#8203;14062](cli/cli#14062)
- Tell agents to use the MR template in AGENTS.md by [@&#8203;williammartin](https://github.com/williammartin) in [#&#8203;14074](cli/cli#14074)
- Make Dependabot triage cheaper and more decisive by [@&#8203;williammartin](https://github.com/williammartin) in [#&#8203;14079](cli/cli#14079)
- Route release deletions through api.Client by [@&#8203;williammartin](https://github.com/williammartin) in [#&#8203;14077](cli/cli#14077)
- Give Dependabot triage a real reachability check by [@&#8203;williammartin](https://github.com/williammartin) in [#&#8203;14087](cli/cli#14087)
- Restore automatic spam issue closure by [@&#8203;williammartin](https://github.com/williammartin) in [#&#8203;14088](cli/cli#14088)
- Add a scheduled tech debt burndown skill by [@&#8203;williammartin](https://github.com/williammartin) in [#&#8203;14095](cli/cli#14095)
- Use reflect.Pointer instead of deprecated reflect.Ptr by [@&#8203;williammartin](https://github.com/williammartin) in [#&#8203;14098](cli/cli#14098)
- Clarify what belongs in the MR template's testing section by [@&#8203;williammartin](https://github.com/williammartin) in [#&#8203;14103](cli/cli#14103)
- Rename cli-code-reviewer skill to code-review by [@&#8203;BagToad](https://github.com/BagToad) in [#&#8203;14116](cli/cli#14116)
- Add aw-actions group to dependabot configuration by [@&#8203;babakks](https://github.com/babakks) in [#&#8203;14123](cli/cli#14123)
- Isolate tests from local machine's auth and git configuration by [@&#8203;BagToad](https://github.com/BagToad) in [#&#8203;14128](cli/cli#14128)
- Don't ask for feature detection cleanup comments when not needed by [@&#8203;babakks](https://github.com/babakks) in [#&#8203;14139](cli/cli#14139)
- Accept pre-release tags in deployment validation by [@&#8203;BagToad](https://github.com/BagToad) in [#&#8203;14193](cli/cli#14193)
- ci: add temporary step to verify Linux repo signing keys by [@&#8203;babakks](https://github.com/babakks) in [#&#8203;14202](cli/cli#14202)
- Revert "ci: add temporary step to verify Linux repo signing keys" by [@&#8203;babakks](https://github.com/babakks) in [#&#8203;14203](cli/cli#14203)
- Fix issue triage action compatibility \[skip changelog] by [@&#8203;tidy-dev](https://github.com/tidy-dev) in [#&#8203;14207](cli/cli#14207)

##### :dependabot: Dependencies

- chore(deps): bump github.com/sigstore/sigstore-go from 1.2.2 to 1.3.0 by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;14047](cli/cli#14047)
- chore(deps): bump the codeql-actions group across 1 directory with 3 updates by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;14049](cli/cli#14049)
- chore(deps): bump google.golang.org/grpc from 1.82.1 to 1.83.0 by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;14048](cli/cli#14048)
- chore(deps): bump github.com/google/go-containerregistry from 0.21.7 to 0.21.8 by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;14066](cli/cli#14066)
- chore(deps): bump actions/attest from 4.2.1 to 4.2.2 by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;14100](cli/cli#14100)
- chore(deps): bump azure/login from 3.0.0 to 3.0.1 by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;14101](cli/cli#14101)
- chore(deps): bump the codeql-actions group across 1 directory with 3 updates by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;14091](cli/cli#14091)
- chore(deps): bump github/gh-aw-actions/setup-cli from 0.83.4 to 0.85.4 by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;14068](cli/cli#14068)
- chore(deps): bump github.com/google/go-containerregistry from 0.21.8 to 0.21.9 by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;14119](cli/cli#14119)
- chore(deps): bump github.com/klauspost/compress from 1.19.1 to 1.19.2 by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;14120](cli/cli#14120)
- chore(deps): bump the aw-actions group with 2 updates by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;14147](cli/cli#14147)
- chore: sign APT repository with both keys by [@&#8203;babakks](https://github.com/babakks) in [#&#8203;13271](cli/cli#13271)
- chore(deps): bump github.com/yuin/goldmark from 1.8.4 to 1.8.5 by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;14029](cli/cli#14029)
- chore(deps): bump actions/attest from 4.2.0 to 4.2.1 by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;14050](cli/cli#14050)
- Bump golangci-lint in CI to v2.12.2 by [@&#8203;williammartin](https://github.com/williammartin) in [#&#8203;14102](cli/cli#14102)
- chore(deps): bump the aw-actions group with 2 updates by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;14124](cli/cli#14124)
- chore(deps): bump google.golang.org/protobuf from 1.36.11 to 1.36.12 by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;14140](cli/cli#14140)
- Upgrade gh-aw workflows to v0.85.4 by [@&#8203;tidy-dev](https://github.com/tidy-dev) in [#&#8203;14141](cli/cli#14141)
- Bump Go to 1.26.6 by [@&#8203;github-actions](https://github.com/github-actions)\[bot] in [#&#8203;14143](cli/cli#14143)
- chore: bump go to 1.26.7 by [@&#8203;babakks](https://github.com/babakks) in [#&#8203;14205](cli/cli#14205)
- chore(deps): bump github.com/stretchr/testify from 1.11.1 to 1.12.1 by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;14204](cli/cli#14204)
- chore(deps): bump the codeql-actions group across 1 directory with 3 updates by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;14169](cli/cli#14169)
- chore(deps): bump golang.org/x/crypto from 0.54.0 to 0.55.0 by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;14164](cli/cli#14164)
- chore(deps): bump charm.land/lipgloss/v2 from 2.0.5 to 2.0.6 by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;14166](cli/cli#14166)
- Bump gh-aw-actions to v0.87.1 and recompile agentic workflows by [@&#8203;BagToad](https://github.com/BagToad) in [#&#8203;14210](cli/cli#14210)

#### New Contributors

- [@&#8203;sergiou87](https://github.com/sergiou87) made their first contribution in [#&#8203;14030](cli/cli#14030)
- [@&#8203;michaeljacholke](https://github.com/michaeljacholke) made their first contribution in [#&#8203;14006](cli/cli#14006)

**Full Changelog**: <cli/cli@v2.97.0...v2.98.0>

</details>

---

### Configuration

📅 **Schedule**: (UTC)

- Branch creation
  - At any time (no schedule defined)
- Automerge
  - At any time (no schedule defined)

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever MR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this MR and you won't be reminded about this update again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this MR, check this box

---

This MR has been generated by [Mend Renovate](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4yODguMCIsInVwZGF0ZWRJblZlciI6IjQzLjI4OC4wIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6WyJSZW5vdmF0ZSBCb3QiLCJhdXRvbWF0aW9uOmJvdC1hdXRob3JlZCIsImRlcGVuZGVuY3ktdHlwZTo6bWlub3IiXX0=-->
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Tell extensions when they are run as extensions and not standalone

3 participants