diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
index a500a4b..bbbc11c 100644
--- a/.github/CODEOWNERS
+++ b/.github/CODEOWNERS
@@ -1,2 +1 @@
* @cloudsmith-io/engineering
-BartoszBlizniak
\ No newline at end of file
diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md
index d24edce..3ecde82 100644
--- a/.github/ISSUE_TEMPLATE/bug_report.md
+++ b/.github/ISSUE_TEMPLATE/bug_report.md
@@ -27,5 +27,13 @@ labels: bug
- **OS**:
- **Script**:
+- **CLI version requested**:
- **Shell**:
- **CI system (if any)**:
+
+## Additional Context
+
+
diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml
index cf4c706..e2edbff 100644
--- a/.github/ISSUE_TEMPLATE/config.yml
+++ b/.github/ISSUE_TEMPLATE/config.yml
@@ -7,5 +7,5 @@ contact_links:
url: https://docs.cloudsmith.com/developer-tools/cli
about: Check the official Cloudsmith CLI documentation
- name: Cloudsmith Support
- url: https://cloudsmith.io/support/
+ url: https://support.cloudsmith.com/hc/en-us/requests/new
about: Contact Cloudsmith support for account-specific issues
diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md
index f68a52b..e852847 100644
--- a/.github/PULL_REQUEST_TEMPLATE.md
+++ b/.github/PULL_REQUEST_TEMPLATE.md
@@ -1,8 +1,8 @@
-# Description
+## Summary
-
+
-## Type of Change
+## Type of change
- [ ] Bug fix
- [ ] New feature
@@ -11,6 +11,21 @@
- [ ] Refactoring
- [ ] Other (please describe)
-## Additional Notes
+## Validation
-
+
+
+## Compatibility
+
+
+
+## Checklist
+
+- [ ] I have added or updated tests where appropriate.
+- [ ] I have updated documentation and `CHANGELOG.md` where appropriate.
+- [ ] I have not included credentials, tokens, or other sensitive data.
+- [ ] I have called out any breaking change explicitly.
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 724c37e..fcb4f57 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -108,13 +108,12 @@ jobs:
fail-fast: false
matrix:
include:
- # TODO confirm vendor path with each integration's maintainers.
- repo: cloudsmith-io/cloudsmith-cli-action
- vendor_path: scripts/vendor/
+ integration: github-action
- repo: cloudsmith-io/orb
- vendor_path: scripts/vendor/
+ integration: circleci-orb
- repo: cloudsmith-io/cloudsmith-ado-integration
- vendor_path: scripts/vendor/
+ integration: azure-devops
steps:
- name: Checkout installer repo
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
@@ -129,33 +128,75 @@ jobs:
set -eu
git clone --depth 1 "https://x-access-token:${VENDOR_TOKEN}@github.com/${TARGET_REPO}.git" target-repo
- - name: Vendor scripts with provenance header
+ - name: Vendor release assets
env:
+ INTEGRATION: ${{ matrix.integration }}
TAG: ${{ github.ref_name }}
- SOURCE_REPO: ${{ github.repository }}
- VENDOR_PATH: ${{ matrix.vendor_path }}
run: |
set -eu
- mkdir -p "target-repo/${VENDOR_PATH}"
- for f in install.sh install.ps1; do
- sha="$(sha256sum "$f" | awk '{print $1}')"
- first_line="$(head -n1 "$f")"
- dest="target-repo/${VENDOR_PATH}${f}"
- {
- case "$first_line" in
- '#!'*) printf '%s\n' "$first_line" ;;
- esac
- printf '# Vendored from %s\n' "$SOURCE_REPO"
- printf '# Source tag: %s\n' "$TAG"
- printf '# Source commit: %s\n' "$GITHUB_SHA"
- printf '# SHA-256 (pre-vendoring): %s\n' "$sha"
- printf '# Do not edit directly; update via the source repository and re-vendor.\n'
- case "$first_line" in
- '#!'*) tail -n +2 "$f" ;;
- *) cat "$f" ;;
- esac
- } > "$dest"
- done
+ case "$INTEGRATION" in
+ github-action)
+ destination="target-repo/installer"
+ mkdir -p "$destination"
+ cp install.sh install.ps1 "$destination/"
+ printf '%s\n' "$TAG" > "$destination/VERSION"
+ (
+ cd "$destination"
+ sha256sum install.sh install.ps1 > SHA256SUMS
+ )
+ ;;
+ circleci-orb)
+ destination="target-repo/src/scripts"
+ mkdir -p "$destination"
+ cp install.sh "$destination/install.sh"
+ sha="$(sha256sum install.sh | awk '{print $1}')"
+ printf 'version=%s\nsha256=%s\n' "$TAG" "$sha" \
+ > "$destination/install.sh.version"
+ ;;
+ azure-devops)
+ destination="target-repo/cloudsmith-task/installer"
+ mkdir -p "$destination"
+ cp install.sh install.ps1 "$destination/"
+ printf '%s\n' "$TAG" > "$destination/VERSION"
+ ;;
+ *)
+ echo "unsupported integration: $INTEGRATION" >&2
+ exit 1
+ ;;
+ esac
+
+ - name: Verify vendored files
+ env:
+ INTEGRATION: ${{ matrix.integration }}
+ run: |
+ set -eu
+ case "$INTEGRATION" in
+ github-action)
+ destination="target-repo/installer"
+ cmp install.sh "$destination/install.sh"
+ cmp install.ps1 "$destination/install.ps1"
+ test -s "$destination/VERSION"
+ (cd "$destination" && sha256sum -c SHA256SUMS)
+ ;;
+ circleci-orb)
+ destination="target-repo/src/scripts"
+ cmp install.sh "$destination/install.sh"
+ expected="$(awk -F= '$1 == "sha256" {print $2}' "$destination/install.sh.version")"
+ actual="$(sha256sum install.sh | awk '{print $1}')"
+ test -n "$(awk -F= '$1 == "version" {print $2}' "$destination/install.sh.version")"
+ test "$expected" = "$actual"
+ ;;
+ azure-devops)
+ destination="target-repo/cloudsmith-task/installer"
+ cmp install.sh "$destination/install.sh"
+ cmp install.ps1 "$destination/install.ps1"
+ test -s "$destination/VERSION"
+ ;;
+ *)
+ echo "unsupported integration: $INTEGRATION" >&2
+ exit 1
+ ;;
+ esac
- name: Commit and open vendoring PR
env:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 033d47d..ae9f001 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -9,6 +9,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added
-- Initial `install.sh` and `install.ps1`.
-- CI: lint, cross-platform tests, zizmor scanning, and a tag-triggered
- release workflow.
+- Cross-platform `install.sh` and `install.ps1` installers for the standalone
+ Cloudsmith CLI.
+- Automatic platform detection and support for selecting a specific CLI
+ version or target.
+- SHA-256 verification, archive safety checks, atomic installation, and
+ concurrent-install locking.
+- Stable machine-readable output for CI/CD consumers.
+- Cross-platform test, security scanning, release, and integration-vendoring
+ workflows.
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 0000000..4312c89
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,68 @@
+# Contributing
+
+Thank you for helping improve the Cloudsmith CLI installers. Bug reports,
+documentation improvements, tests, and focused code changes are welcome.
+
+## Before you start
+
+- Search existing issues and pull requests before opening a duplicate.
+- Use the issue templates for bugs, feature requests, and usage questions.
+- Do not open a public issue for a suspected vulnerability. Follow
+ [SECURITY.md](SECURITY.md) instead.
+- Keep changes focused. The installers are vendored by multiple CI/CD
+ integrations, so compatibility and reviewability matter.
+
+## Development setup
+
+Fork and clone the repository, then create a branch from `main`.
+
+The shell suite requires bats-core 1.5 or later and Python 3:
+
+```sh
+bats tests/
+```
+
+Shell changes should also pass the checks used in CI:
+
+```sh
+sh -n install.sh
+shellcheck --shell=sh --severity=style install.sh
+```
+
+The Windows suite requires Pester 5.5 through 5.99:
+
+```powershell
+Install-Module Pester -MinimumVersion 5.5 -MaximumVersion 5.99 -Scope CurrentUser
+Import-Module Pester -MinimumVersion 5.5 -MaximumVersion 5.99
+Invoke-Pester -Path tests/Install.Tests.ps1
+```
+
+See [tests/README.md](tests/README.md) for more detail. Tests use local fixtures
+and do not require access to the public package repository.
+
+## Compatibility expectations
+
+Treat these interfaces as public contracts:
+
+- command-line options and PowerShell parameters
+- `CLOUDSMITH_CLI_*` environment variables
+- supported target identifiers
+- versioned installation layout
+- the four `key=value` output lines
+
+Changes to a public contract need explicit release notes, integration impact
+analysis, and an appropriate semantic-versioning decision. Preserve POSIX `sh`
+compatibility in `install.sh` and Windows PowerShell 5.1 compatibility in
+`install.ps1`.
+
+## Pull requests
+
+- Explain the problem and the chosen solution.
+- Add or update tests for behavior changes.
+- Update `README.md` and `CHANGELOG.md` when user-visible behavior changes.
+- List the validation commands you ran.
+- Keep commits free of credentials, private URLs, generated test artifacts, and
+ unrelated changes.
+
+Maintainers may request additional integration validation before merging a
+change that affects installer behavior or release distribution.
diff --git a/README.md b/README.md
index fe3c1a2..7b00ce7 100644
--- a/README.md
+++ b/README.md
@@ -1,91 +1,192 @@
-# cloudsmith-cli-install-script
+
-Install scripts for the [Cloudsmith CLI](https://github.com/cloudsmith-io/cloudsmith-cli)
-standalone binary: `install.sh` (POSIX sh) and `install.ps1` (PowerShell).
+# Cloudsmith CLI installer
-Each script detects the platform, downloads the matching release archive,
-verifies its SHA-256, and installs it into a versioned directory. It prints
-where the binary was installed; it does not modify `PATH` or authenticate.
+Secure, cross-platform installation scripts for the standalone Cloudsmith CLI.
-These scripts are vendored into the Cloudsmith CI integrations (GitHub
-Action, CircleCI orb, Azure DevOps task). They are also published as assets
-on this repository's GitHub releases, alongside a `SHA256SUMS` file.
+[](https://github.com/cloudsmith-io/cloudsmith-cli-install-script/actions/workflows/test.yml)
+[](https://github.com/cloudsmith-io/cloudsmith-cli-install-script/actions/workflows/zizmor.yml)
+[](LICENSE)
-## Usage
+[Quick start](#quick-start) · [Configuration](#configuration) · [Supported targets](#supported-targets) · [Security](#security) · [Support](#support)
-Linux/macOS:
+
+
+This repository is the canonical source for the scripts used to install the
+[Cloudsmith CLI](https://github.com/cloudsmith-io/cloudsmith-cli) standalone
+binary:
+
+- `install.sh` for Linux and macOS using POSIX `sh`
+- `install.ps1` for Windows using PowerShell
+
+The scripts detect the host platform, download the matching release archive,
+verify its SHA-256 checksum, and install the CLI into a versioned directory.
+They do not modify `PATH` or authenticate with Cloudsmith.
+
+> [!IMPORTANT]
+> For reproducible CI builds, use a specific CLI version instead of `latest`.
+
+## At a glance
+
+| Capability | Behaviour |
+| --- | --- |
+| Platforms | Linux, macOS, and Windows on x86-64 and Arm where supported |
+| Integrity | Verifies the release archive against its SHA-256 manifest |
+| Installation | Uses versioned directories and atomic activation |
+| Output | Emits four stable `key=value` lines for CI consumers |
+| Side effects | Does not modify `PATH` or configure authentication |
+| Distribution | Published as release assets and vendored into Cloudsmith CI/CD integrations |
+
+## Quick start
+
+Download the appropriate script from a tagged
+[GitHub release](https://github.com/cloudsmith-io/cloudsmith-cli-install-script/releases)
+and verify it against the release's `SHA256SUMS` file before running it.
+
+### Linux or macOS
```sh
-sh install.sh --version latest
+sh ./install.sh --version latest
```
-Windows:
+### Windows
```powershell
./install.ps1 -Version latest
```
-### Options
+On success, use the reported `bin_dir` to update `PATH`, or invoke the reported
+`executable` directly.
-`install.sh` flag / `install.ps1` parameter, matching environment variable, default:
+## How installation works
-| sh | PowerShell | Env var | Default |
+```mermaid
+flowchart LR
+ A[Detect host] --> B[Resolve CLI release]
+ B --> C[Download archive]
+ C --> D[Verify SHA-256]
+ D --> E[Validate contents]
+ E --> F[Install atomically]
+```
+
+An existing verified installation is reused unless a forced reinstall is
+requested. Concurrent installations are serialized with a lock.
+
+## Requirements
+
+| Installer | Supported host | Required tools |
+| --- | --- | --- |
+| `install.sh` | Linux or macOS | `curl`, or GNU `wget` with `--https-only`; `tar`; `gzip`; and one of `sha256sum`, `shasum`, or `openssl` |
+| `install.ps1` | Windows x86-64, or Windows Arm64 with x86-64 emulation | Windows PowerShell 5.1 or PowerShell 7+ |
+
+## Configuration
+
+Command-line options take precedence over environment variables.
+
+| Shell option | PowerShell parameter | Environment variable | Default |
| --- | --- | --- | --- |
| `--version` | `-Version` | `CLOUDSMITH_CLI_VERSION` | `latest` |
-| `--install-root` | `-InstallRoot` | `CLOUDSMITH_CLI_INSTALL_ROOT` | `~/.local/share/cloudsmith-cli` (sh), `%LOCALAPPDATA%\Cloudsmith\CLI` (ps1) |
-| `--target` | `-Target` | `CLOUDSMITH_CLI_TARGET` | auto-detected |
-| `--output-file` | `-OutputFile` | `CLOUDSMITH_CLI_OUTPUT_FILE` | stdout |
-| `--repository` | `-Repository` | `CLOUDSMITH_CLI_REPOSITORY` | `cloudsmith/cli-standalone` |
-| `--manifest-url` | `-ManifestUrl` | `CLOUDSMITH_CLI_MANIFEST_URL` | derived from repository/version/target |
-| `--force` | `-Force` | — | off |
+| `--install-root` | `-InstallRoot` | `CLOUDSMITH_CLI_INSTALL_ROOT` | Shell: `$XDG_DATA_HOME/cloudsmith-cli` or `~/.local/share/cloudsmith-cli`
PowerShell: `%LOCALAPPDATA%\Cloudsmith\CLI` |
+| `--target` | `-Target` | `CLOUDSMITH_CLI_TARGET` | Automatically detected |
+| `--output-file` | `-OutputFile` | `CLOUDSMITH_CLI_OUTPUT_FILE` | Standard output |
+| `--repository` | `-Repository` | `CLOUDSMITH_CLI_REPOSITORY` | `cloudsmith/cli` |
+| `--manifest-url` | `-ManifestUrl` | `CLOUDSMITH_CLI_MANIFEST_URL` | Derived from the repository, version, and target |
+| `--force` | `-Force` | Not applicable | Disabled |
-## Output
+Run `sh ./install.sh --help` for shell usage. PowerShell parameters support
+standard `Get-Help` and tab completion.
-On success the script prints exactly four `key=value` lines (to stdout, or
-to the output file if set). Everything else goes to stderr.
+
+Advanced overrides
-```
+The repository, manifest URL, and target overrides are intended for testing or
+unusual environments. Normal installations should use the defaults.
+
+If libc detection fails on an unusual Linux host, set the target explicitly
+with `--target` or `CLOUDSMITH_CLI_TARGET`.
+
+
+
+## Output contract
+
+Each installer emits exactly four `key=value` lines to standard output, or to
+the requested output file. Diagnostic messages are written to standard error.
+
+```text
version=1.19.0
target=linux-x86_64-gnu
bin_dir=/home/runner/.local/share/cloudsmith-cli/1.19.0/linux-x86_64-gnu/cloudsmith
executable=/home/runner/.local/share/cloudsmith-cli/1.19.0/linux-x86_64-gnu/cloudsmith/cloudsmith
```
-## Supported targets
+> [!NOTE]
+> The four keys and their order form a public contract for CI/CD integrations.
-| Target | Notes |
-| --- | --- |
-| `linux-x86_64-gnu` | glibc 2.28+ |
-| `linux-aarch64-gnu` | glibc 2.28+ |
-| `linux-x86_64-musl` | Alpine and other musl distros |
-| `linux-aarch64-musl` | Alpine and other musl distros |
-| `macos-x86_64` | Intel |
-| `macos-arm64` | Apple Silicon |
-| `windows-x86_64` | Also used on Windows ARM64 (x86_64 emulation) |
+## Supported targets
-If libc detection fails on an unusual host, pass `--target` explicitly.
+| Operating system | Architecture | Target | Runtime notes |
+| --- | --- | --- | --- |
+| Linux | x86-64 | `linux-x86_64-gnu` | glibc 2.28+ |
+| Linux | Arm64 | `linux-aarch64-gnu` | glibc 2.28+ |
+| Linux | x86-64 | `linux-x86_64-musl` | Alpine and other musl distributions |
+| Linux | Arm64 | `linux-aarch64-musl` | Alpine and other musl distributions |
+| macOS | x86-64 | `macos-x86_64` | Intel |
+| macOS | Arm64 | `macos-arm64` | Apple silicon |
+| Windows | x86-64 | `windows-x86_64` | Also used on Windows Arm64 through x86-64 emulation |
## Security
-- HTTPS-only downloads.
-- Archive SHA-256 verified against the release manifest before extraction.
-- Archive entries validated before extraction (no absolute paths or `..`).
-- Binary is staged, checked with `cloudsmith --version`, then activated
- atomically; concurrent installs are serialized with a lock.
-- Already-installed, verified versions are reused unless `--force` is given.
+The installers apply the following safeguards:
+
+- HTTPS-only downloads
+- SHA-256 verification before extraction
+- archive validation to reject absolute paths and parent-directory traversal
+- executable validation with `cloudsmith --version` before activation
+- atomic activation and serialized concurrent installs
+- reuse only after validating an existing installation
+
+The shell installer uses `curl` or `wget`; the PowerShell installer uses the
+.NET web request stack. Standard proxy configuration and operating-system
+certificate trust stores apply.
+
+To report a potential vulnerability, follow the [security policy](SECURITY.md)
+instead of opening a public issue.
+
+## CI/CD integrations
-Proxies and custom CAs: downloads use `curl`/`wget` (sh) or
-`Invoke-WebRequest` (PowerShell), so standard proxy environment variables
-and OS trust stores apply.
+Tagged releases publish `install.sh`, `install.ps1`, and `SHA256SUMS`. The
+approved installer bytes are also vendored into:
+
+| Integration | Installer consumer |
+| --- | --- |
+| GitHub Actions | [cloudsmith-cli-action](https://github.com/cloudsmith-io/cloudsmith-cli-action) |
+| CircleCI | [Cloudsmith orb](https://github.com/cloudsmith-io/orb) |
+| Azure DevOps | Cloudsmith CLI task for Azure Pipelines |
## Development
-```sh
-bats tests/ # shell suite (needs bats-core and python3)
-Invoke-Pester # PowerShell suite (Pester 5.5+, Windows)
-```
+See [CONTRIBUTING.md](CONTRIBUTING.md) for the development workflow and
+[tests/README.md](tests/README.md) for test-suite requirements.
+
+## Releases and compatibility
+
+Releases use `vX.Y.Z` tags, and published release assets are immutable.
+Breaking changes to command-line options, environment variables, installation
+layout, supported targets, or the output contract require a major version bump.
+
+## Support
+
+| Need help with | Where to go |
+| --- | --- |
+| Installer bug | [Open a bug report](https://github.com/cloudsmith-io/cloudsmith-cli-install-script/issues/new?template=bug_report.md) |
+| Installer usage | [Ask a question](https://github.com/cloudsmith-io/cloudsmith-cli-install-script/issues/new?template=question.md) |
+| Cloudsmith CLI behaviour | [Cloudsmith CLI repository](https://github.com/cloudsmith-io/cloudsmith-cli) |
+| Account or private support | [Submit a Cloudsmith support request](https://support.cloudsmith.com/hc/en-us/requests/new) |
+| Security concern | [Follow the private reporting process](SECURITY.md) |
+
+See [SUPPORT.md](SUPPORT.md) for the information to include when requesting
+help.
-## Releases
+## License
-Tagged `vX.Y.Z`. Release assets are immutable; breaking changes to flags or
-output are a major version bump.
+Licensed under the [Apache License 2.0](LICENSE).
diff --git a/SECURITY.md b/SECURITY.md
new file mode 100644
index 0000000..2f859bb
--- /dev/null
+++ b/SECURITY.md
@@ -0,0 +1,25 @@
+# Security policy
+
+## Supported versions
+
+Security fixes are applied to the latest released installer version. Users
+should upgrade to the latest release before reporting an issue that may already
+have been addressed.
+
+## Reporting a vulnerability
+
+Do not disclose suspected vulnerabilities in a public GitHub issue, discussion,
+or pull request.
+
+Report them privately through
+[Cloudsmith Support](https://support.cloudsmith.com/hc/en-us/requests/new). Include:
+
+- the installer script and release version
+- the operating system and architecture
+- clear reproduction steps or a proof of concept
+- the potential impact
+- any suggested remediation
+
+Remove API keys, tokens, credentials, private repository URLs, and other
+sensitive data from logs and examples. Cloudsmith will coordinate disclosure
+and remediation with the reporter.
diff --git a/SUPPORT.md b/SUPPORT.md
new file mode 100644
index 0000000..a4e3f27
--- /dev/null
+++ b/SUPPORT.md
@@ -0,0 +1,18 @@
+# Support
+
+Use the channel that best matches the problem:
+
+- For reproducible installer bugs, open a
+ [bug report](https://github.com/cloudsmith-io/cloudsmith-cli-install-script/issues/new?template=bug_report.md).
+- For installer usage questions, open a
+ [question](https://github.com/cloudsmith-io/cloudsmith-cli-install-script/issues/new?template=question.md).
+- For problems with the installed CLI rather than its installation, use the
+ [Cloudsmith CLI repository](https://github.com/cloudsmith-io/cloudsmith-cli).
+- For account-specific or private help, contact
+ [Cloudsmith Support](https://support.cloudsmith.com/hc/en-us/requests/new).
+- For suspected vulnerabilities, follow [SECURITY.md](SECURITY.md) and do not
+ open a public issue.
+
+When reporting an installer problem, include the script release, requested CLI
+version, operating system, architecture, shell, exact command, and stderr
+output. Remove all credentials, tokens, private URLs, and other sensitive data.
diff --git a/tests/README.md b/tests/README.md
index ccc1cb2..2558ba6 100644
--- a/tests/README.md
+++ b/tests/README.md
@@ -1,10 +1,34 @@
# Tests
-Bash suite (`install.bats`, `detect_target.bats`): requires [bats-core](https://github.com/bats-core/bats-core) >= 1.5.0 and python3.
-Run: `bats tests/`
+Both test suites are network-free. Fixtures are served over loopback only
+(`127.0.0.1` or `localhost`).
-PowerShell suite (`Install.Tests.ps1`), Windows only: requires Pester >= 5.5.0
-(`Install-Module Pester -MinimumVersion 5.5.0 -Scope CurrentUser`).
-Run: `Invoke-Pester -Path tests`. Self-skips cleanly on non-Windows hosts.
+## Shell suite
-Both suites are network-free: all fixtures are served over loopback only (127.0.0.1 / localhost).
+The shell suite covers `install.bats` and `detect_target.bats`. It requires
+[bats-core](https://github.com/bats-core/bats-core) 1.5.0 or later and
+Python 3.
+
+Run it from the repository root:
+
+```sh
+bats tests/
+```
+
+## PowerShell suite
+
+The Windows-only PowerShell suite (`Install.Tests.ps1`) requires Pester 5.5 or
+later in the 5.x release line. The suite skips cleanly on non-Windows hosts.
+
+Install the supported Pester version:
+
+```powershell
+Install-Module Pester -MinimumVersion 5.5 -MaximumVersion 5.99 -Scope CurrentUser
+```
+
+Run the suite from the repository root:
+
+```powershell
+Import-Module Pester -MinimumVersion 5.5 -MaximumVersion 5.99
+Invoke-Pester -Path tests/Install.Tests.ps1
+```