Skip to content

Commit

Permalink
[SM-1130] - add install scripts (#645)
Browse files Browse the repository at this point in the history
## Type of change

- [ ] Bug fix
- [ ] New feature development
- [ ] Tech debt (refactoring, code cleanup, dependency upgrades, etc)
- [x] Build/deploy pipeline (DevOps)
- [x] Other

## Objective

Allow easy installation of `bws` on most systems with:

`curl <script-url> | sh` on Linux and macOS
or
`iwr <script-url> | iex` on Windows

## Code changes

- **crates/bws/scripts/install.(sh|ps1):** added install scripts that
will download `bws` from GitHub Releases, validate the checksums, and
install it. I am unsure if this is the _best_ place to house these
scripts and would welcome suggestions for other places these might
belong.
- **.github/workflows/version-bump.yml:** added version bumps for the
install scripts

## Before you submit

- Please add **unit tests** where it makes sense to do so
  • Loading branch information
tangowithfoxtrot authored Jun 28, 2024
1 parent 05475f1 commit 56547d7
Show file tree
Hide file tree
Showing 4 changed files with 313 additions and 4 deletions.
9 changes: 6 additions & 3 deletions .github/workflows/version-bump.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
20 changes: 19 additions & 1 deletion crates/bws/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
108 changes: 108 additions & 0 deletions crates/bws/scripts/install.ps1
Original file line number Diff line number Diff line change
@@ -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
}
180 changes: 180 additions & 0 deletions crates/bws/scripts/install.sh
Original file line number Diff line number Diff line change
@@ -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 "$@"

0 comments on commit 56547d7

Please sign in to comment.