diff --git a/.github/workflows/version-bump.yml b/.github/workflows/version-bump.yml index 406f3b927..9d69818e7 100644 --- a/.github/workflows/version-bump.yml +++ b/.github/workflows/version-bump.yml @@ -103,16 +103,19 @@ jobs: run: cargo set-version -p bitwarden-napi ${{ inputs.version_number }} ### bitwarden - - name: Bump bitwarden crate Version if: ${{ inputs.project == 'bitwarden' }} run: cargo set-version -p bitwarden ${{ inputs.version_number }} ### bws - - name: Bump bws Version if: ${{ inputs.project == 'bws' }} - run: cargo set-version -p bws ${{ inputs.version_number }} + run: | + cargo set-version -p bws ${{ inputs.version_number }} + # bump the version in install.sh + sed -i 's/DEFAULT_BWS_VERSION="[0-9]\+\.[0-9]\+\.[0-9]\+"/DEFAULT_BWS_VERSION="${{ inputs.version_number }}"/' ./crates/bws/scripts/install.sh + # bump the version in install.ps1 + sed -i 's/\$defaultBwsVersion = "[0-9]\+\.[0-9]\+\.[0-9]\+"/\$defaultBwsVersion = "${{ inputs.version_number }}"/' ./crates/bws/scripts/install.ps1 ### python - name: Bump python-sdk Version diff --git a/crates/bws/README.md b/crates/bws/README.md index ace5210f1..2b9c8c99c 100644 --- a/crates/bws/README.md +++ b/crates/bws/README.md @@ -6,11 +6,29 @@ and might be missing some functionality. ## Install +We offer three ways to install bws: + +### Cargo (crates.io) + +Download bws via `cargo` from [crates.io](https://crates.io): + ```bash cargo install bws ``` -Or download a pre-built binary from the [Releases](https://github.com/bitwarden/sdk/releases) page. +### Install Script (from GitHub Releases) + +Linux/macOS: `curl https://bws.bitwarden.com/install | sh` + +Windows: `iwr https://bws.bitwarden.com/install | iex` + +An optional `-u/--uninstall` flag can be passed to the POSIX script to uninstall the CLI. The +PowerShell version accepts an equivalent `-Uninstall` flag. The uninstallation process will remove +the `bws` binary and the configuration directory (`~/.bws`). + +### GitHub Releases (Manual) + +Download a pre-built binary from the [Releases](https://github.com/bitwarden/sdk/releases) page. ## Usage diff --git a/crates/bws/scripts/install.ps1 b/crates/bws/scripts/install.ps1 new file mode 100755 index 000000000..f39846c20 --- /dev/null +++ b/crates/bws/scripts/install.ps1 @@ -0,0 +1,108 @@ +param ( + [switch]$Uninstall +) + +$ErrorActionPreference = "Stop" + +$defaultBwsVersion = "0.5.0" +$bwsVersion = if ($env:bwsVersion) { $env:bwsVersion } else { $defaultBwsVersion } +$installDir = [Environment]::GetFolderPath([Environment+SpecialFolder]::LocalApplicationData) | Join-Path -ChildPath "Programs" | Join-Path -ChildPath "Bitwarden" + +# https://learn.microsoft.com/en-us/windows/win32/cimwin32prov/win32-processor#properties +$processorArch = (Get-CimInstance -ClassName Win32_Processor).Architecture +if ($processorArch -eq 9) { + $arch = "x86_64" +} elseif ($processorArch -eq 12) { + $arch = "aarch64" +} else { + throw "Unsupported architecture: $processorArch" +} + +function Test-BwsInstallation { + $existingBws = Get-Command bws -ErrorAction SilentlyContinue + if ($null -ne $existingBws) { + $userInput = Read-Host "bws is already installed at $($existingBws.Source). Do you want to overwrite it? (Y/N)" + if ($userInput -ne "Y") { + Write-Host "Installation cancelled by user." + exit + } + } +} + +function Invoke-BwsDownload { + Write-Host "Detected architecture: $arch" + + $bwsUrl = "https://github.com/bitwarden/sdk/releases/download/bws-v$bwsVersion/bws-$arch-pc-windows-msvc-$bwsVersion.zip" + Write-Host "Downloading bws from: $bwsUrl" + $outputPath = Join-Path $env:TEMP "bws.zip" + Invoke-WebRequest -Uri $bwsUrl -OutFile $outputPath + return $outputPath +} + +function Test-Checksum { + param($zipPath) + Write-Host "Validating checksum..." + + $checksumUrl = "https://github.com/bitwarden/sdk/releases/download/bws-v$bwsVersion/bws-sha256-checksums-$bwsVersion.txt" + $checksumFile = Join-Path $env:TEMP "bws-checksums.txt" + Invoke-WebRequest -Uri $checksumUrl -OutFile $checksumFile + + $expectedChecksum = (Get-Content $checksumFile | Where-Object { $_ -match "bws-$arch-pc-windows-msvc-$bwsVersion.zip" }).Split(" ")[0] + $actualChecksum = (Get-FileHash -Algorithm SHA256 -Path $zipPath).Hash + + if ($actualChecksum -ne $expectedChecksum) { + throw "Checksum validation failed. Expected: $expectedChecksum, Actual: $actualChecksum" + } else { + Write-Host "Checksum validation successful." + } +} + +function Install-Bws { + param($zipPath) + Write-Host "Installing bws..." + New-Item -ItemType Directory -Force -Path $installDir | Out-Null + Expand-Archive -Force $zipPath -DestinationPath $installDir + Write-Host "bws installed to $installDir" + setx PATH "$env:PATH;$installDir" + Write-Host "$installDir has been added to your PATH" + Write-Host "Please restart your shell to use bws" +} + +function Test-Bws { + Write-Host "Checking bws..." + $bwsPath = Join-Path $installDir "bws.exe" + if (Test-Path $bwsPath) { + Write-Host "bws is installed at $bwsPath" + } else { + throw "bws is not installed" + } +} + +function Remove-Bws { + Write-Host "Uninstalling bws..." + + if (Test-Path $installDir) { + Remove-Item -Path $installDir -Recurse -Force + Write-Host "bws uninstalled from $installDir" + } else { + Write-Host "bws installation directory not found at $installDir. Skipping removal." + } + + $configDir = "$env:USERPROFILE\.bws" + if (Test-Path $configDir -PathType Container) { + Remove-Item -Path $configDir -Recurse -Force + Write-Host "bws config directory removed from $configDir" + } else { + Write-Host "bws config directory not found at $configDir. Skipping removal." + } +} + +if ($Uninstall) { + Remove-Bws +} else { + Test-BwsInstallation + $zipPath = Invoke-BwsDownload + Test-Checksum -zipPath $zipPath + Install-Bws -zipPath $zipPath + Test-Bws +} diff --git a/crates/bws/scripts/install.sh b/crates/bws/scripts/install.sh new file mode 100755 index 000000000..126ae9e22 --- /dev/null +++ b/crates/bws/scripts/install.sh @@ -0,0 +1,180 @@ +#!/bin/sh + +################################################## +# An installer for the bws command line utility. # +################################################## + +DEFAULT_BWS_VERSION="0.5.0" +BWS_VERSION="${BWS_VERSION:-$DEFAULT_BWS_VERSION}" + +main() { + case "$1" in + -u | --uninstall) + uninstall_bws + ;; + *) + check_required + platform_detect + arch_detect + download_bws + validate_checksum + install_bws + ;; + esac +} + +error() { + echo "$1" >&2 + echo "Exiting..." >&2 + exit 1 +} + +check_required() { + if ! command -v curl >/dev/null && ! command -v wget >/dev/null; then + error "curl or wget is required to download bws." + fi + + if ! command -v unzip >/dev/null; then + error "unzip is required to install bws." + fi +} + +can_sudo() { + if command -v sudo >/dev/null; then + echo "Attempting to install bws with sudo. Please enter your password if prompted." + if sudo -v 2>/dev/null; then + echo "sudo is available and we have the necessary permissions." + echo "Installing bws to /usr/local/bin..." + return 0 + else + echo "sudo is available, but we failed to authenticate." + return 1 + fi + else + echo "sudo is not available." + return 1 + fi +} + +platform_detect() { + if [ "$(uname -s)" = "Linux" ]; then + PLATFORM="unknown-linux-gnu" + elif [ "$(uname -s)" = "Darwin" ]; then + PLATFORM="apple-darwin" + else + error "Unsupported platform: $(uname -s)" + fi +} + +arch_detect() { + if [ "$(uname -m)" = "x86_64" ]; then + ARCH="x86_64" + elif [ "$(uname -m)" = "aarch64" ]; then # Linux uname output + ARCH="aarch64" + elif [ "$(uname -m)" = "arm64" ]; then # Darwin uname output + ARCH="aarch64" + else + error "Unsupported architecture: $(uname -m)" + fi +} + +checksum() { + if command -v sha256sum >/dev/null; then + sha256sum "$1" + else + shasum -a 256 "$1" + fi +} + +downloader() { + if command -v curl >/dev/null; then + curl -L -o "$2" "$1" + else + wget -O "$2" "$1" + fi +} + +extract() { + unzip -o "$1" -d "$2" +} + +download_bws() { + bws_url="https://github.com/bitwarden/sdk/releases/download/bws-v${BWS_VERSION}/bws-${ARCH}-${PLATFORM}-${BWS_VERSION}.zip" + echo "Downloading bws from: $bws_url" + tmp_dir="$(mktemp -d)" + downloader "$bws_url" "$tmp_dir/bws.zip" +} + +validate_checksum() { + checksum_url="https://github.com/bitwarden/sdk/releases/download/bws-v${BWS_VERSION}/bws-sha256-checksums-${BWS_VERSION}.txt" + echo "Downloading checksum file from: $checksum_url" + checksum_file="$tmp_dir/bws-checksums.txt" + downloader "$checksum_url" "$checksum_file" + + expected_checksum="$(grep "bws-${ARCH}-${PLATFORM}-${BWS_VERSION}.zip" "$checksum_file" | awk '{print $1}')" + actual_checksum="$(checksum "$tmp_dir/bws.zip" | awk '{print $1}')" + + if [ "$actual_checksum" != "$expected_checksum" ]; then + error "Checksum validation failed. Expected: $expected_checksum, Actual: $actual_checksum" + else + echo "Checksum validation successful." + fi +} + +install_bws() { + echo "Installing bws..." + extract "$tmp_dir/bws.zip" "$tmp_dir" + chmod +x "$tmp_dir/bws" + + if can_sudo; then + sudo install -m 755 "$tmp_dir/bws" /usr/local/bin/bws + + if ! command -v bws >/dev/null; then + error "Installation failed. bws was not found in /usr/local/bin" + fi + + echo "bws installed to /usr/local/bin/bws" + else + echo "Installing to your \$HOME directory..." + user_bin_dir="${HOME}/.local/bin" + mkdir -p "${user_bin_dir}" + install -m 755 "$tmp_dir/bws" "${user_bin_dir}/bws" + + if ! command -v "${user_bin_dir}/bws" >/dev/null; then + error "Installation failed. bws was not found in ${user_bin_dir}" + fi + + echo "bws installed at ${user_bin_dir}/bws" + echo "Please add ${user_bin_dir} to your PATH by adding the following line to your ~/.profile or shell rc file:" + echo "export PATH=\"\$PATH:${user_bin_dir}\"" + fi + + rm -rf "$tmp_dir" +} + +uninstall_bws() { + if command -v bws >/dev/null; then + echo "Uninstalling bws..." + if can_sudo; then + sudo rm "$(command -v bws)" + else + rm "$(command -v bws)" + fi + + # Safely remove the configuration directory + if [ -n "$HOME" ]; then + echo "Removing bws configuration directory at ${HOME}/.bws" + echo "If you use another directory for your configuration, you may want to remove it manually." + rm -rf "${HOME}/.bws" + else + echo "HOME environment variable is not set. Cannot safely remove .bws directory." + fi + + echo "bws uninstalled successfully." + else + echo "bws is not installed." + fi + exit 0 +} + +main "$@"