Repository: reubeno/brush (rust)

Issue:
Proposal: Provide a brush-specific variable
## Summary:

Add `$BRUSH_VERSION` or another global shell variable to indicate that the shell is brush.

## Motivation:

I want this feature for the gradual adoption of brush, as a workaround for compatibility issues such as #380. Until these compatibility issues are resolved, loading the same `.bashrc` file generates warnings or errors.
We can avoid this behavior by putting a special configuration file for brush; however, managing shared and brush-specific configurations is often inconvenient.
On the other hand, ignoring the whole configuration with `--norc` or `--noprofile` is also not desirable for a daily driver.
So, initially, I added a condition to skip unsupported features with `! command -v brushinfo`. However, this approach often significantly slows down original bash startup.
I'm not sure why, but I suspect it's due to the much longer `$PATH` in my use case (NixOS and/or home-manager. Windows also injects `$PATH` in WSL2). Actually, my bash startup speed increased to 140ms from 90ms.

## Justification:

As far as I know, other famous shells have similar features.

* bash: `$BASH_VERSION`
* zsh: `$ZSH_VERSION`
* fish: `$FISH_VERSION`
* nushell: `$env.NU_VERSION`
* powershell: `$PSVersionTable`
* rusty_bash: This is an exception. Sush does not have a special version variable and [returns its own version via `$BASH_VERSION`](https://github.com/shellgei/rusty_bash/blob/1d34a3145a27eb8092763eb53b7b691a405ea3b5/src/core.rs#L124-L135).

Currently, brush seems to return `$BASH_VERSION`, but this is not its own version. I think keeping the current `$BASH_VERSION` and providing a brush-specific variable like `$BRUSH_VERSION` will be useful for gradual adoption.



Pull request:
feat(env): introduce brush specific variable
Resolves #530

I wasn't sure how to add appropriate tests for this feature. For now, I've ensured that if there's a major mistake, the integration tests will fail early.

Patch:
diff --git a/.github/workflows/cd.yaml b/.github/workflows/cd.yaml
index 5bcaf32..b494d57 100644
--- a/.github/workflows/cd.yaml
+++ b/.github/workflows/cd.yaml
@@ -61,22 +61,22 @@ jobs:
 
     steps:
       - name: "Checkout repository"
-        uses: actions/checkout@v4.2.2
+        uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
         with:
           persist-credentials: false
 
       - name: "Install Rust toolchain"
-        uses: actions-rust-lang/setup-rust-toolchain@v1.12.0
+        uses: actions-rust-lang/setup-rust-toolchain@9d7e65c320fdb52dcd45ffaa68deb6c02c8754d9 # v1.12.0
 
       - name: "Setup cross-compiling toolchain"
         if: startsWith(matrix.os, 'ubuntu') && !contains(matrix.target, '-musl')
-        uses: taiki-e/setup-cross-toolchain-action@v1.29.1
+        uses: taiki-e/setup-cross-toolchain-action@0123528f956f923e7a476f4cc04882bc005e7c89 # v1.29.1
         with:
           target: ${{ matrix.target }}
 
       - name: "Installing musl cross tools"
         if: contains(matrix.target, '-musl')
-        uses: taiki-e/install-action@v2.50.0
+        uses: taiki-e/install-action@84c20235bedc3797c7e1ddf685c574a4a6c666da # v2.52.2
         with:
           tool: cross
 
@@ -85,7 +85,7 @@ jobs:
         run: echo "RUSTFLAGS=${RUSTFLAGS} -C target-feature=+crt-static" >> "${GITHUB_ENV}"
 
       - name: "Build and upload binaries to release"
-        uses: taiki-e/upload-rust-binary-action@v1.26.0
+        uses: taiki-e/upload-rust-binary-action@db101489b509ad1c7acce163e118eb36a1650f98 # v1.26.0
         id: upload-release
         with:
           dry-run: ${{ github.event_name != 'release' }}
@@ -99,6 +99,6 @@ jobs:
 
       - name: "Generate artifact attestation"
         if: github.event_name == 'release'
-        uses: actions/attest-build-provenance@v2.3.0
+        uses: actions/attest-build-provenance@db473fddc028af60658334401dc6fa3ffd8669fd # v2.3.0
         with:
           subject-path: "${{ steps.upload-release.outputs.archive }}.*"
diff --git a/.github/workflows/ci-reports.yaml b/.github/workflows/ci-reports.yaml
index 5835225..3df02fd 100644
--- a/.github/workflows/ci-reports.yaml
+++ b/.github/workflows/ci-reports.yaml
@@ -20,7 +20,7 @@ jobs:
         id: find-run
         env:
           GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
-        uses: octokit/request-action@v2.x
+        uses: octokit/request-action@dad4362715b7fb2ddedf9772c8670824af564f0d # v2.4.0
         with:
           route: GET /repos/${{ github.event.workflow_run.repository.full_name }}/actions/runs/${{ github.event.workflow_run.id }}
 
@@ -54,7 +54,7 @@ jobs:
       - name: Download code coverage reports
         if: steps.get-pr.outputs.pr_number != ''
         continue-on-error: true
-        uses: actions/download-artifact@v4
+        uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
         with:
           pattern: codecov-reports*
           merge-multiple: true
@@ -65,7 +65,7 @@ jobs:
       - name: Download performance reports
         if: steps.get-pr.outputs.pr_number != ''
         continue-on-error: true
-        uses: actions/download-artifact@v4
+        uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
         with:
           pattern: perf-reports*
           merge-multiple: true
@@ -76,7 +76,7 @@ jobs:
       - name: Download test results
         if: steps.get-pr.outputs.pr_number != ''
         continue-on-error: true
-        uses: actions/download-artifact@v4
+        uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
         with:
           pattern: test-reports*
           merge-multiple: true
@@ -106,7 +106,7 @@ jobs:
              }' >event-file.json
 
       - name: "Publish test results"
-        uses: EnricoMi/publish-unit-test-result-action@v2
+        uses: EnricoMi/publish-unit-test-result-action@3a74b2957438d0b6e2e61d67b05318aa25c9e6c6 # v2.20.0
         if: steps.get-pr.outputs.pr_number != ''
         continue-on-error: true
         with:
@@ -116,7 +116,7 @@ jobs:
           files: reports/test-results-*.xml
 
       - name: "Publish available .md reports to PR"
-        uses: marocchino/sticky-pull-request-comment@v2
+        uses: marocchino/sticky-pull-request-comment@67d0dec7b07ed060a405f9b2a64b8ab319fdd7db # v2.9.2
         if: steps.get-pr.outputs.pr_number != ''
         with:
           path: reports/*.md
diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml
index c4d54df..fee8b16 100644
--- a/.github/workflows/ci.yaml
+++ b/.github/workflows/ci.yaml
@@ -94,7 +94,9 @@ jobs:
     runs-on: ${{ matrix.host }}
     steps:
       - name: Checkout
-        uses: actions/checkout@v4
+        uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+        with:
+          persist-credentials: false
 
       - name: Set up rust toolchain
         uses: dtolnay/rust-toolchain@stable
@@ -103,7 +105,7 @@ jobs:
           targets: ${{ matrix.target }}
 
       - name: Enable cargo cache
-        uses: Swatinem/rust-cache@v2
+        uses: Swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 # v2.7.8
         with:
           key: "${{ matrix.target }}"
 
@@ -113,7 +115,7 @@ jobs:
 
       - name: Install cross-compilation toolchain
         if: ${{ matrix.target != '' }}
-        uses: taiki-e/install-action@v2
+        uses: taiki-e/install-action@84c20235bedc3797c7e1ddf685c574a4a6c666da # v2.52.2
         with:
           tool: cross
 
@@ -126,14 +128,14 @@ jobs:
         run: cross build --release --target=${{ matrix.target }} ${{ matrix.extra_build_args }}
 
       - name: "Upload binaries"
-        uses: actions/upload-artifact@v4
+        uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
         with:
           name: binaries-${{ matrix.arch }}-${{ matrix.os }}
           path: target/${{ matrix.target }}/release/${{ matrix.binary_name }}
 
       - name: "Upload integration test binaries"
         if: ${{ matrix.target == '' }}
-        uses: actions/upload-artifact@v4
+        uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
         with:
           name: integration-tests-${{ matrix.arch }}-${{ matrix.os }}
           path: |
@@ -168,7 +170,9 @@ jobs:
     runs-on: ${{ matrix.host }}
     steps:
       - name: Checkout
-        uses: actions/checkout@v4
+        uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+        with:
+          persist-credentials: false
 
       - name: Set up rust toolchain
         uses: dtolnay/rust-toolchain@stable
@@ -177,25 +181,25 @@ jobs:
           components: llvm-tools-preview
 
       - name: Enable cargo cache
-        uses: Swatinem/rust-cache@v2
+        uses: Swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 # v2.7.8
         with:
           # Needed to make sure cargo-deny is correctly cached.
           cache-all-crates: true
 
       - name: Install cargo-nextest
-        uses: taiki-e/install-action@v2
+        uses: taiki-e/install-action@84c20235bedc3797c7e1ddf685c574a4a6c666da # v2.52.2
         with:
           tool: cargo-nextest
 
       - name: Install cargo-llvm-cov
-        uses: taiki-e/install-action@v2
+        uses: taiki-e/install-action@84c20235bedc3797c7e1ddf685c574a4a6c666da # v2.52.2
         with:
           tool: cargo-llvm-cov
 
       - name: Set up Homebrew
         if: ${{ matrix.homebrew_supported }}
         id: set-up-homebrew
-        uses: Homebrew/actions/setup-homebrew@master
+        uses: Homebrew/actions/setup-homebrew@b04401e20b1aef21843d7c3e261d27a89f65af7d
         with:
           stable: true
 
@@ -210,8 +214,9 @@ jobs:
           echo "BASH_PATH=${BASH_PATH}">>$GITHUB_ENV
 
       - name: "Download recent bash-completion sources for tests"
-        uses: actions/checkout@v4
+        uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
         with:
+          persist-credentials: false
           repository: "scop/bash-completion"
           ref: "2.16.0"
           path: "bash-completion"
@@ -242,14 +247,14 @@ jobs:
           exit ${result}
 
       - name: "Upload test results"
-        uses: actions/upload-artifact@v4
+        uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
         if: always()
         with:
           name: test-reports${{ matrix.artifact_suffix }}
           path: test-results-*.xml
 
       - name: "Generate code coverage report"
-        uses: clearlyip/code-coverage-report-action@v5
+        uses: clearlyip/code-coverage-report-action@17b6103ac2d3b9bb6c7a8562ac22a4c010d6d52f # v5.2.0
         if: always()
         id: "code_coverage_report"
         with:
@@ -263,7 +268,7 @@ jobs:
           negative_difference_threshold: 5
 
       - name: "Upload code coverage report"
-        uses: actions/upload-artifact@v4
+        uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
         if: always()
         with:
           name: codecov-reports${{ matrix.artifact_suffix }}
@@ -281,7 +286,9 @@ jobs:
 
     steps:
       - name: Checkout
-        uses: actions/checkout@v4
+        uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+        with:
+          persist-credentials: false
 
       - name: Set up rust toolchain (${{ matrix.rust-version }})
         uses: dtolnay/rust-toolchain@master
@@ -290,7 +297,7 @@ jobs:
           components: clippy, rustfmt
 
       - name: Enable cargo cache
-        uses: Swatinem/rust-cache@v2
+        uses: Swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 # v2.7.8
         with:
           # Needed to make sure cargo-deny is correctly cached.
           cache-all-crates: true
@@ -302,7 +309,7 @@ jobs:
         run: cargo check --all-features --all-targets
 
       - name: Install cargo-deny
-        uses: taiki-e/install-action@v2
+        uses: taiki-e/install-action@84c20235bedc3797c7e1ddf685c574a4a6c666da # v2.52.2
         with:
           tool: cargo-deny
 
@@ -319,7 +326,9 @@ jobs:
     runs-on: ubuntu-24.04
     steps:
       - name: Checkout
-        uses: actions/checkout@v4
+        uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+        with:
+          persist-credentials: false
 
       - name: Set up nightly rust toolchain
         uses: dtolnay/rust-toolchain@master
@@ -327,7 +336,7 @@ jobs:
           toolchain: nightly
 
       - name: Install cargo-udeps
-        uses: taiki-e/install-action@v2
+        uses: taiki-e/install-action@84c20235bedc3797c7e1ddf685c574a4a6c666da # v2.52.2
         with:
           tool: cargo-udeps
 
@@ -341,13 +350,15 @@ jobs:
     runs-on: ubuntu-24.04
     steps:
       - name: Checkout
-        uses: actions/checkout@v4
+        uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
         with:
+          persist-credentials: false
           path: pr
 
       - name: Checkout
-        uses: actions/checkout@v4
+        uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
         with:
+          persist-credentials: false
           path: main
           ref: main
 
@@ -357,7 +368,7 @@ jobs:
           toolchain: stable
 
       - name: Enable cargo cache
-        uses: Swatinem/rust-cache@v2
+        uses: Swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 # v2.7.8
         with:
           workspaces: |
             ./pr
@@ -376,7 +387,7 @@ jobs:
           ./pr/scripts/compare-benchmark-results.py -b main/benchmarks.txt -t pr/benchmarks.txt >benchmark-results.md
 
       - name: Upload performance results
-        uses: actions/upload-artifact@v4
+        uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
         with:
           name: perf-reports
           path: |
@@ -391,19 +402,21 @@ jobs:
     needs: build
     steps:
       - name: Checkout brush
-        uses: actions/checkout@v4
+        uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
         with:
+          persist-credentials: false
           path: "brush"
 
       - name: Checkout bash-completion
-        uses: actions/checkout@v4
+        uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
         with:
+          persist-credentials: false
           repository: "scop/bash-completion"
           ref: "2.16.0"
           path: "bash-completion"
 
       - name: Download prebuilt brush binaries
-        uses: actions/download-artifact@v4
+        uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
         with:
           name: "binaries-x86_64-linux"
           path: "binaries"
@@ -443,7 +456,7 @@ jobs:
             >${{ github.workspace }}/test-results-bash-completion.md
 
       - name: Upload test report
-        uses: actions/upload-artifact@v4
+        uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
         with:
           name: test-reports-bash-completion
           path: |
@@ -514,18 +527,19 @@ jobs:
 
       # Checkout sources for YAML-based test cases
       - name: Checkout
-        uses: actions/checkout@v4
+        uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
         with:
+          persist-credentials: false
           path: sources
 
       - name: Download prebuilt brush binaries
-        uses: actions/download-artifact@v4
+        uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
         with:
           name: binaries-x86_64-linux
           path: binaries
 
       - name: Download integration test binaries
-        uses: actions/download-artifact@v4
+        uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
         with:
           name: integration-tests-x86_64-linux
           path: binaries
diff --git a/.github/workflows/devcontainer.yaml b/.github/workflows/devcontainer.yaml
index e699a67..1a5bf2a 100644
--- a/.github/workflows/devcontainer.yaml
+++ b/.github/workflows/devcontainer.yaml
@@ -16,17 +16,19 @@ jobs:
       packages: read
     steps:
       - name: Checkout sources
-        uses: actions/checkout@v4
+        uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+        with:
+          persist-credentials: false
 
       - name: Login to GitHub Container Registry
-        uses: docker/login-action@v3
+        uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3.4.0
         with:
           registry: ghcr.io
           username: ${{ github.repository_owner }}
           password: ${{ secrets.GITHUB_TOKEN }}
 
       - name: Pre-build dev container image
-        uses: devcontainers/ci@v0.3
+        uses: devcontainers/ci@8bf61b26e9c3a98f69cb6ce2f88d24ff59b785c6 # v0.3.1900000417
         with:
           imageName: ghcr.io/reubeno/brush/devcontainer
           imageTag: latest
@@ -41,17 +43,19 @@ jobs:
       packages: write
     steps:
       - name: Checkout sources
-        uses: actions/checkout@v4
+        uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+        with:
+          persist-credentials: false
 
       - name: Login to GitHub Container Registry
-        uses: docker/login-action@v3
+        uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3.4.0
         with:
           registry: ghcr.io
           username: ${{ github.repository_owner }}
           password: ${{ secrets.GITHUB_TOKEN }}
 
       - name: Pre-build dev container image
-        uses: devcontainers/ci@v0.3
+        uses: devcontainers/ci@8bf61b26e9c3a98f69cb6ce2f88d24ff59b785c6 # v0.3.1900000417
         with:
           imageName: ghcr.io/reubeno/brush/devcontainer
           imageTag: latest
diff --git a/.github/workflows/docs.yaml b/.github/workflows/docs.yaml
index 37be412..7b701b0 100644
--- a/.github/workflows/docs.yaml
+++ b/.github/workflows/docs.yaml
@@ -12,6 +12,9 @@ env:
   CLICOLOR: 1
   CLICOLOR_FORCE: 1
 
+permissions:
+  contents: read
+
 jobs:
   # Check links in docs
   check-links:
@@ -19,10 +22,12 @@ jobs:
     runs-on: ubuntu-24.04
     steps:
       - name: Checkout sources
-        uses: actions/checkout@v4
+        uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+        with:
+          persist-credentials: false
 
       - name: Check repo-local links
-        uses: lycheeverse/lychee-action@v2
+        uses: lycheeverse/lychee-action@82202e5e9c2f4ef1a55a3d02563e1cb6041e5332 # v2.4.1
         with:
           args: ". --offline --verbose --no-progress"
           fail: true
@@ -33,7 +38,9 @@ jobs:
     runs-on: ubuntu-24.04
     steps:
       - name: Checkout
-        uses: actions/checkout@v4
+        uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+        with:
+          persist-credentials: false
 
       - name: Set up rust toolchain
         uses: dtolnay/rust-toolchain@stable
@@ -41,7 +48,7 @@ jobs:
           toolchain: stable
 
       - name: Enable cargo cache
-        uses: Swatinem/rust-cache@v2
+        uses: Swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 # v2.7.8
 
       - name: Create dirs
         run: mkdir -p ./md ./man
@@ -50,7 +57,7 @@ jobs:
         run: cargo xtask generate-markdown --out ./md/brush.md
 
       - name: Upload markdown docs
-        uses: actions/upload-artifact@v4
+        uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
         with:
           name: docs-markdown
           path: md
@@ -59,7 +66,7 @@ jobs:
         run: cargo xtask generate-man --output-dir ./man
 
       - name: Upload man pages
-        uses: actions/upload-artifact@v4
+        uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
         with:
           name: docs-man
           path: man
diff --git a/.github/workflows/spelling.yaml b/.github/workflows/spelling.yaml
index d8f8f99..1e06dcf 100644
--- a/.github/workflows/spelling.yaml
+++ b/.github/workflows/spelling.yaml
@@ -16,7 +16,9 @@ jobs:
     runs-on: ubuntu-latest
     steps:
       - name: Checkout brush
-        uses: actions/checkout@v4
+        uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+        with:
+          persist-credentials: false
 
       - name: Spell check repo
-        uses: crate-ci/typos@master
+        uses: crate-ci/typos@0f0ccba9ed1df83948f0c15026e4f5ccfce46109 # v1.32.0
diff --git a/brush-core/src/shell.rs b/brush-core/src/shell.rs
index 87fbad5..5c584c1 100644
--- a/brush-core/src/shell.rs
+++ b/brush-core/src/shell.rs
@@ -189,6 +189,8 @@ pub struct CreateOptions {
     pub max_function_call_depth: Option<usize>,
     /// Key bindings helper for the shell to use.
     pub key_bindings: Option<KeyBindingsHelper>,
+    /// Brush implementation version.
+    pub shell_version: Option<String>,
 }
 
 /// Represents an executing script.
@@ -300,6 +302,11 @@ impl Shell {
                 self.env.set_global(k, var)?;
             }
         }
+        let shell_version = options.shell_version.clone();
+        self.env.set_global(
+            "BRUSH_VERSION",
+            ShellVariable::new(shell_version.unwrap_or_default().into()),
+        )?;
 
         // TODO(#479): implement $_
 
@@ -409,6 +416,7 @@ impl Shell {
         self.env.set_global("BASH_VERSINFO", bash_versinfo_var)?;
 
         // BASH_VERSION
+        // This is the Bash interface version. See BRUSH_VERSION for its implementation version.
         self.env.set_global(
             "BASH_VERSION",
             ShellVariable::new(
diff --git a/brush-shell/src/main.rs b/brush-shell/src/main.rs
index 315977f..d3b370c 100644
--- a/brush-shell/src/main.rs
+++ b/brush-shell/src/main.rs
@@ -262,6 +262,7 @@ async fn instantiate_shell(
             verbose: args.verbose,
             max_function_call_depth: None,
             key_bindings: None,
+            shell_version: Some(env!("CARGO_PKG_VERSION").to_string()),
         },
         disable_bracketed_paste: args.disable_bracketed_paste,
         disable_color: args.disable_color,


Test patch:
diff --git a/brush-shell/tests/integration_tests.rs b/brush-shell/tests/integration_tests.rs
new file mode 100644
index 0000000..f6ec2b1
--- /dev/null
+++ b/brush-shell/tests/integration_tests.rs
@@ -0,0 +1,34 @@
+//! Integration tests for brush shell
+
+// For now, only compile this for Unix-like platforms (Linux, macOS).
+#![cfg(unix)]
+#![allow(clippy::panic_in_result_fn)]
+
+use anyhow::Context;
+
+#[test]
+fn get_version_variables() -> anyhow::Result<()> {
+    let shell_path = assert_cmd::cargo::cargo_bin("brush");
+    let brush_ver_str = get_variable(&shell_path, "BRUSH_VERSION")?;
+    let bash_ver_str = get_variable(&shell_path, "BASH_VERSION")?;
+
+    assert_eq!(brush_ver_str, env!("CARGO_PKG_VERSION"));
+    assert_ne!(
+        brush_ver_str, bash_ver_str,
+        "Should differ for scripting use-case"
+    );
+
+    Ok(())
+}
+
+fn get_variable(shell_path: &std::path::Path, var: &str) -> anyhow::Result<String> {
+    let output = std::process::Command::new(shell_path)
+        .arg("--norc")
+        .arg("--noprofile")
+        .arg("-c")
+        .arg(format!("echo -n ${{{var}}}"))
+        .output()
+        .with_context(|| format!("failed to retrieve {var}"))?
+        .stdout;
+    Ok(String::from_utf8(output)?)
+}